[
  {
    "path": ".commitlintrc.json",
    "content": "{\n  \"extends\": [\"@commitlint/config-conventional\"]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/eslintrc\",\n  \"root\": true,\n  \"extends\": [\n    \"next/core-web-vitals\",\n    \"prettier\",\n    \"plugin:tailwindcss/recommended\"\n  ],\n  \"plugins\": [\"tailwindcss\"],\n  \"rules\": {\n    \"@next/next/no-html-link-for-pages\": \"off\",\n    \"react/jsx-key\": \"off\",\n    \"tailwindcss/no-custom-classname\": \"off\",\n    \"tailwindcss/classnames-order\": \"error\"\n  },\n  \"settings\": {\n    \"tailwindcss\": {\n      \"callees\": [\"cn\"],\n      \"config\": \"tailwind.config.js\"\n    },\n    \"next\": {\n      \"rootDir\": true\n    }\n  },\n  \"overrides\": [\n    {\n      \"files\": [\"*.ts\", \"*.tsx\"],\n      \"parser\": \"@typescript-eslint/parser\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# local env files\n.env*.local\n.env\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n\n.vscode\n.contentlayer"
  },
  {
    "path": ".husky/commit-msg",
    "content": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx commitlint --edit $1\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx pretty-quick --staged\n"
  },
  {
    "path": ".nvmrc",
    "content": "v16.18.0\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist\nnode_modules\n.next\nbuild\n.contentlayer"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2022 shadcn\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Taxonomy\n\nAn open source application built using the new router, server components and everything new in Next.js 13.\n\n> **Warning**\n> This app is a work in progress. I'm building this in public. You can follow the progress on Twitter [@shadcn](https://twitter.com/shadcn).\n> See the roadmap below.\n\n## About this project\n\nThis project as an experiment to see how a modern app (with features like authentication, subscriptions, API routes, static pages for docs ...etc) would work in Next.js 13 and server components.\n\n**This is not a starter template.**\n\nA few people have asked me to turn this into a starter. I think we could do that once the new features are out of beta.\n\n## Note on Performance\n\n> **Warning**\n> This app is using the unstable releases for Next.js 13 and React 18. The new router and app dir is still in beta and not production-ready.\n> **Expect some performance hits when testing the dashboard**.\n> If you see something broken, you can ping me [@shadcn](https://twitter.com/shadcn).\n\n## Features\n\n- New `/app` dir,\n- Routing, Layouts, Nested Layouts and Layout Groups\n- Data Fetching, Caching and Mutation\n- Loading UI\n- Route handlers\n- Metadata files\n- Server and Client Components\n- API Routes and Middlewares\n- Authentication using **NextAuth.js**\n- ORM using **Prisma**\n- Database on **PlanetScale**\n- UI Components built using **Radix UI**\n- Documentation and blog using **MDX** and **Contentlayer**\n- Subscriptions using **Stripe**\n- Styled using **Tailwind CSS**\n- Validations using **Zod**\n- Written in **TypeScript**\n\n## Roadmap\n\n- [x] ~Add MDX support for basic pages~\n- [x] ~Build marketing pages~\n- [x] ~Subscriptions using Stripe~\n- [x] ~Responsive styles~\n- [x] ~Add OG image for blog using @vercel/og~\n- [x] Dark mode\n\n## Known Issues\n\nA list of things not working right now:\n\n1. ~GitHub authentication (use email)~\n2. ~[Prisma: Error: ENOENT: no such file or directory, open '/var/task/.next/server/chunks/schema.prisma'](https://github.com/prisma/prisma/issues/16117)~\n3. ~[Next.js 13: Client side navigation does not update head](https://github.com/vercel/next.js/issues/42414)~\n4. [Cannot use opengraph-image.tsx inside catch-all routes](https://github.com/vercel/next.js/issues/48162)\n\n## Why not tRPC, Turborepo or X?\n\nI might add this later. For now, I want to see how far we can get using Next.js only.\n\nIf you have some suggestions, feel free to create an issue.\n\n## Running Locally\n\n1. Install dependencies using pnpm:\n\n```sh\npnpm install\n```\n\n2. Copy `.env.example` to `.env.local` and update the variables.\n\n```sh\ncp .env.example .env.local\n```\n\n3. Start the development server:\n\n```sh\npnpm dev\n```\n\n## License\n\nLicensed under the [MIT license](https://github.com/shadcn/taxonomy/blob/main/LICENSE.md).\n"
  },
  {
    "path": "app/(auth)/layout.tsx",
    "content": "interface AuthLayoutProps {\n  children: React.ReactNode\n}\n\nexport default function AuthLayout({ children }: AuthLayoutProps) {\n  return <div className=\"min-h-screen\">{children}</div>\n}\n"
  },
  {
    "path": "app/(auth)/login/page.tsx",
    "content": "import { Metadata } from \"next\"\nimport Link from \"next/link\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Icons } from \"@/components/icons\"\nimport { UserAuthForm } from \"@/components/user-auth-form\"\n\nexport const metadata: Metadata = {\n  title: \"Login\",\n  description: \"Login to your account\",\n}\n\nexport default function LoginPage() {\n  return (\n    <div className=\"container flex h-screen w-screen flex-col items-center justify-center\">\n      <Link\n        href=\"/\"\n        className={cn(\n          buttonVariants({ variant: \"ghost\" }),\n          \"absolute left-4 top-4 md:left-8 md:top-8\"\n        )}\n      >\n        <>\n          <Icons.chevronLeft className=\"mr-2 h-4 w-4\" />\n          Back\n        </>\n      </Link>\n      <div className=\"mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]\">\n        <div className=\"flex flex-col space-y-2 text-center\">\n          <Icons.logo className=\"mx-auto h-6 w-6\" />\n          <h1 className=\"text-2xl font-semibold tracking-tight\">\n            Welcome back\n          </h1>\n          <p className=\"text-sm text-muted-foreground\">\n            Enter your email to sign in to your account\n          </p>\n        </div>\n        <UserAuthForm />\n        <p className=\"px-8 text-center text-sm text-muted-foreground\">\n          <Link\n            href=\"/register\"\n            className=\"hover:text-brand underline underline-offset-4\"\n          >\n            Don&apos;t have an account? Sign Up\n          </Link>\n        </p>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(auth)/register/page.tsx",
    "content": "import Link from \"next/link\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Icons } from \"@/components/icons\"\nimport { UserAuthForm } from \"@/components/user-auth-form\"\n\nexport const metadata = {\n  title: \"Create an account\",\n  description: \"Create an account to get started.\",\n}\n\nexport default function RegisterPage() {\n  return (\n    <div className=\"container grid h-screen w-screen flex-col items-center justify-center lg:max-w-none lg:grid-cols-2 lg:px-0\">\n      <Link\n        href=\"/login\"\n        className={cn(\n          buttonVariants({ variant: \"ghost\" }),\n          \"absolute right-4 top-4 md:right-8 md:top-8\"\n        )}\n      >\n        Login\n      </Link>\n      <div className=\"hidden h-full bg-muted lg:block\" />\n      <div className=\"lg:p-8\">\n        <div className=\"mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]\">\n          <div className=\"flex flex-col space-y-2 text-center\">\n            <Icons.logo className=\"mx-auto h-6 w-6\" />\n            <h1 className=\"text-2xl font-semibold tracking-tight\">\n              Create an account\n            </h1>\n            <p className=\"text-sm text-muted-foreground\">\n              Enter your email below to create your account\n            </p>\n          </div>\n          <UserAuthForm />\n          <p className=\"px-8 text-center text-sm text-muted-foreground\">\n            By clicking continue, you agree to our{\" \"}\n            <Link\n              href=\"/terms\"\n              className=\"hover:text-brand underline underline-offset-4\"\n            >\n              Terms of Service\n            </Link>{\" \"}\n            and{\" \"}\n            <Link\n              href=\"/privacy\"\n              className=\"hover:text-brand underline underline-offset-4\"\n            >\n              Privacy Policy\n            </Link>\n            .\n          </p>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/billing/loading.tsx",
    "content": "import { CardSkeleton } from \"@/components/card-skeleton\"\nimport { DashboardHeader } from \"@/components/header\"\nimport { DashboardShell } from \"@/components/shell\"\n\nexport default function DashboardBillingLoading() {\n  return (\n    <DashboardShell>\n      <DashboardHeader\n        heading=\"Billing\"\n        text=\"Manage billing and your subscription plan.\"\n      />\n      <div className=\"grid gap-10\">\n        <CardSkeleton />\n      </div>\n    </DashboardShell>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/billing/page.tsx",
    "content": "import { redirect } from \"next/navigation\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { getCurrentUser } from \"@/lib/session\"\nimport { stripe } from \"@/lib/stripe\"\nimport { getUserSubscriptionPlan } from \"@/lib/subscription\"\nimport { Alert, AlertDescription, AlertTitle } from \"@/components/ui/alert\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { BillingForm } from \"@/components/billing-form\"\nimport { DashboardHeader } from \"@/components/header\"\nimport { Icons } from \"@/components/icons\"\nimport { DashboardShell } from \"@/components/shell\"\n\nexport const metadata = {\n  title: \"Billing\",\n  description: \"Manage billing and your subscription plan.\",\n}\n\nexport default async function BillingPage() {\n  const user = await getCurrentUser()\n\n  if (!user) {\n    redirect(authOptions?.pages?.signIn || \"/login\")\n  }\n\n  const subscriptionPlan = await getUserSubscriptionPlan(user.id)\n\n  // If user has a pro plan, check cancel status on Stripe.\n  let isCanceled = false\n  if (subscriptionPlan.isPro && subscriptionPlan.stripeSubscriptionId) {\n    const stripePlan = await stripe.subscriptions.retrieve(\n      subscriptionPlan.stripeSubscriptionId\n    )\n    isCanceled = stripePlan.cancel_at_period_end\n  }\n\n  return (\n    <DashboardShell>\n      <DashboardHeader\n        heading=\"Billing\"\n        text=\"Manage billing and your subscription plan.\"\n      />\n      <div className=\"grid gap-8\">\n        <Alert className=\"!pl-14\">\n          <Icons.warning />\n          <AlertTitle>This is a demo app.</AlertTitle>\n          <AlertDescription>\n            Taxonomy app is a demo app using a Stripe test environment. You can\n            find a list of test card numbers on the{\" \"}\n            <a\n              href=\"https://stripe.com/docs/testing#cards\"\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"font-medium underline underline-offset-8\"\n            >\n              Stripe docs\n            </a>\n            .\n          </AlertDescription>\n        </Alert>\n        <BillingForm\n          subscriptionPlan={{\n            ...subscriptionPlan,\n            isCanceled,\n          }}\n        />\n      </div>\n    </DashboardShell>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/layout.tsx",
    "content": "import { notFound } from \"next/navigation\"\n\nimport { dashboardConfig } from \"@/config/dashboard\"\nimport { getCurrentUser } from \"@/lib/session\"\nimport { MainNav } from \"@/components/main-nav\"\nimport { DashboardNav } from \"@/components/nav\"\nimport { SiteFooter } from \"@/components/site-footer\"\nimport { UserAccountNav } from \"@/components/user-account-nav\"\n\ninterface DashboardLayoutProps {\n  children?: React.ReactNode\n}\n\nexport default async function DashboardLayout({\n  children,\n}: DashboardLayoutProps) {\n  const user = await getCurrentUser()\n\n  if (!user) {\n    return notFound()\n  }\n\n  return (\n    <div className=\"flex min-h-screen flex-col space-y-6\">\n      <header className=\"sticky top-0 z-40 border-b bg-background\">\n        <div className=\"container flex h-16 items-center justify-between py-4\">\n          <MainNav items={dashboardConfig.mainNav} />\n          <UserAccountNav\n            user={{\n              name: user.name,\n              image: user.image,\n              email: user.email,\n            }}\n          />\n        </div>\n      </header>\n      <div className=\"container grid flex-1 gap-12 md:grid-cols-[200px_1fr]\">\n        <aside className=\"hidden w-[200px] flex-col md:flex\">\n          <DashboardNav items={dashboardConfig.sidebarNav} />\n        </aside>\n        <main className=\"flex w-full flex-1 flex-col overflow-hidden\">\n          {children}\n        </main>\n      </div>\n      <SiteFooter className=\"border-t\" />\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/loading.tsx",
    "content": "import { DashboardHeader } from \"@/components/header\"\nimport { PostCreateButton } from \"@/components/post-create-button\"\nimport { PostItem } from \"@/components/post-item\"\nimport { DashboardShell } from \"@/components/shell\"\n\nexport default function DashboardLoading() {\n  return (\n    <DashboardShell>\n      <DashboardHeader heading=\"Posts\" text=\"Create and manage posts.\">\n        <PostCreateButton />\n      </DashboardHeader>\n      <div className=\"divide-border-200 divide-y rounded-md border\">\n        <PostItem.Skeleton />\n        <PostItem.Skeleton />\n        <PostItem.Skeleton />\n        <PostItem.Skeleton />\n        <PostItem.Skeleton />\n      </div>\n    </DashboardShell>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/page.tsx",
    "content": "import { redirect } from \"next/navigation\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { db } from \"@/lib/db\"\nimport { getCurrentUser } from \"@/lib/session\"\nimport { EmptyPlaceholder } from \"@/components/empty-placeholder\"\nimport { DashboardHeader } from \"@/components/header\"\nimport { PostCreateButton } from \"@/components/post-create-button\"\nimport { PostItem } from \"@/components/post-item\"\nimport { DashboardShell } from \"@/components/shell\"\n\nexport const metadata = {\n  title: \"Dashboard\",\n}\n\nexport default async function DashboardPage() {\n  const user = await getCurrentUser()\n\n  if (!user) {\n    redirect(authOptions?.pages?.signIn || \"/login\")\n  }\n\n  const posts = await db.post.findMany({\n    where: {\n      authorId: user.id,\n    },\n    select: {\n      id: true,\n      title: true,\n      published: true,\n      createdAt: true,\n    },\n    orderBy: {\n      updatedAt: \"desc\",\n    },\n  })\n\n  return (\n    <DashboardShell>\n      <DashboardHeader heading=\"Posts\" text=\"Create and manage posts.\">\n        <PostCreateButton />\n      </DashboardHeader>\n      <div>\n        {posts?.length ? (\n          <div className=\"divide-y divide-border rounded-md border\">\n            {posts.map((post) => (\n              <PostItem key={post.id} post={post} />\n            ))}\n          </div>\n        ) : (\n          <EmptyPlaceholder>\n            <EmptyPlaceholder.Icon name=\"post\" />\n            <EmptyPlaceholder.Title>No posts created</EmptyPlaceholder.Title>\n            <EmptyPlaceholder.Description>\n              You don&apos;t have any posts yet. Start creating content.\n            </EmptyPlaceholder.Description>\n            <PostCreateButton variant=\"outline\" />\n          </EmptyPlaceholder>\n        )}\n      </div>\n    </DashboardShell>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/settings/loading.tsx",
    "content": "import { Card } from \"@/components/ui/card\"\nimport { CardSkeleton } from \"@/components/card-skeleton\"\nimport { DashboardHeader } from \"@/components/header\"\nimport { DashboardShell } from \"@/components/shell\"\n\nexport default function DashboardSettingsLoading() {\n  return (\n    <DashboardShell>\n      <DashboardHeader\n        heading=\"Settings\"\n        text=\"Manage account and website settings.\"\n      />\n      <div className=\"grid gap-10\">\n        <CardSkeleton />\n      </div>\n    </DashboardShell>\n  )\n}\n"
  },
  {
    "path": "app/(dashboard)/dashboard/settings/page.tsx",
    "content": "import { redirect } from \"next/navigation\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { getCurrentUser } from \"@/lib/session\"\nimport { DashboardHeader } from \"@/components/header\"\nimport { DashboardShell } from \"@/components/shell\"\nimport { UserNameForm } from \"@/components/user-name-form\"\n\nexport const metadata = {\n  title: \"Settings\",\n  description: \"Manage account and website settings.\",\n}\n\nexport default async function SettingsPage() {\n  const user = await getCurrentUser()\n\n  if (!user) {\n    redirect(authOptions?.pages?.signIn || \"/login\")\n  }\n\n  return (\n    <DashboardShell>\n      <DashboardHeader\n        heading=\"Settings\"\n        text=\"Manage account and website settings.\"\n      />\n      <div className=\"grid gap-10\">\n        <UserNameForm user={{ id: user.id, name: user.name || \"\" }} />\n      </div>\n    </DashboardShell>\n  )\n}\n"
  },
  {
    "path": "app/(docs)/docs/[[...slug]]/page.tsx",
    "content": "import { notFound } from \"next/navigation\"\nimport { allDocs } from \"contentlayer/generated\"\n\nimport { getTableOfContents } from \"@/lib/toc\"\nimport { Mdx } from \"@/components/mdx-components\"\nimport { DocsPageHeader } from \"@/components/page-header\"\nimport { DocsPager } from \"@/components/pager\"\nimport { DashboardTableOfContents } from \"@/components/toc\"\n\nimport \"@/styles/mdx.css\"\nimport { Metadata } from \"next\"\n\nimport { env } from \"@/env.mjs\"\nimport { absoluteUrl } from \"@/lib/utils\"\n\ninterface DocPageProps {\n  params: {\n    slug: string[]\n  }\n}\n\nasync function getDocFromParams(params) {\n  const slug = params.slug?.join(\"/\") || \"\"\n  const doc = allDocs.find((doc) => doc.slugAsParams === slug)\n\n  if (!doc) {\n    null\n  }\n\n  return doc\n}\n\nexport async function generateMetadata({\n  params,\n}: DocPageProps): Promise<Metadata> {\n  const doc = await getDocFromParams(params)\n\n  if (!doc) {\n    return {}\n  }\n\n  const url = env.NEXT_PUBLIC_APP_URL\n\n  const ogUrl = new URL(`${url}/api/og`)\n  ogUrl.searchParams.set(\"heading\", doc.description ?? doc.title)\n  ogUrl.searchParams.set(\"type\", \"Documentation\")\n  ogUrl.searchParams.set(\"mode\", \"dark\")\n\n  return {\n    title: doc.title,\n    description: doc.description,\n    openGraph: {\n      title: doc.title,\n      description: doc.description,\n      type: \"article\",\n      url: absoluteUrl(doc.slug),\n      images: [\n        {\n          url: ogUrl.toString(),\n          width: 1200,\n          height: 630,\n          alt: doc.title,\n        },\n      ],\n    },\n    twitter: {\n      card: \"summary_large_image\",\n      title: doc.title,\n      description: doc.description,\n      images: [ogUrl.toString()],\n    },\n  }\n}\n\nexport async function generateStaticParams(): Promise<\n  DocPageProps[\"params\"][]\n> {\n  return allDocs.map((doc) => ({\n    slug: doc.slugAsParams.split(\"/\"),\n  }))\n}\n\nexport default async function DocPage({ params }: DocPageProps) {\n  const doc = await getDocFromParams(params)\n\n  if (!doc) {\n    notFound()\n  }\n\n  const toc = await getTableOfContents(doc.body.raw)\n\n  return (\n    <main className=\"relative py-6 lg:gap-10 lg:py-10 xl:grid xl:grid-cols-[1fr_300px]\">\n      <div className=\"mx-auto w-full min-w-0\">\n        <DocsPageHeader heading={doc.title} text={doc.description} />\n        <Mdx code={doc.body.code} />\n        <hr className=\"my-4 md:my-6\" />\n        <DocsPager doc={doc} />\n      </div>\n      <div className=\"hidden text-sm xl:block\">\n        <div className=\"sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-10\">\n          <DashboardTableOfContents toc={toc} />\n        </div>\n      </div>\n    </main>\n  )\n}\n"
  },
  {
    "path": "app/(docs)/docs/layout.tsx",
    "content": "import { docsConfig } from \"@/config/docs\"\nimport { DocsSidebarNav } from \"@/components/sidebar-nav\"\n\ninterface DocsLayoutProps {\n  children: React.ReactNode\n}\n\nexport default function DocsLayout({ children }: DocsLayoutProps) {\n  return (\n    <div className=\"flex-1 md:grid md:grid-cols-[220px_1fr] md:gap-6 lg:grid-cols-[240px_1fr] lg:gap-10\">\n      <aside className=\"fixed top-14 z-30 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 overflow-y-auto border-r py-6 pr-2 md:sticky md:block lg:py-10\">\n        <DocsSidebarNav items={docsConfig.sidebarNav} />\n      </aside>\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(docs)/guides/[...slug]/page.tsx",
    "content": "import Link from \"next/link\"\nimport { notFound } from \"next/navigation\"\nimport { allGuides } from \"contentlayer/generated\"\n\nimport { getTableOfContents } from \"@/lib/toc\"\nimport { Icons } from \"@/components/icons\"\nimport { Mdx } from \"@/components/mdx-components\"\nimport { DocsPageHeader } from \"@/components/page-header\"\nimport { DashboardTableOfContents } from \"@/components/toc\"\n\nimport \"@/styles/mdx.css\"\nimport { Metadata } from \"next\"\n\nimport { env } from \"@/env.mjs\"\nimport { absoluteUrl, cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\n\ninterface GuidePageProps {\n  params: {\n    slug: string[]\n  }\n}\n\nasync function getGuideFromParams(params) {\n  const slug = params?.slug?.join(\"/\")\n  const guide = allGuides.find((guide) => guide.slugAsParams === slug)\n\n  if (!guide) {\n    null\n  }\n\n  return guide\n}\n\nexport async function generateMetadata({\n  params,\n}: GuidePageProps): Promise<Metadata> {\n  const guide = await getGuideFromParams(params)\n\n  if (!guide) {\n    return {}\n  }\n\n  const url = env.NEXT_PUBLIC_APP_URL\n\n  const ogUrl = new URL(`${url}/api/og`)\n  ogUrl.searchParams.set(\"heading\", guide.title)\n  ogUrl.searchParams.set(\"type\", \"Guide\")\n  ogUrl.searchParams.set(\"mode\", \"dark\")\n\n  return {\n    title: guide.title,\n    description: guide.description,\n    openGraph: {\n      title: guide.title,\n      description: guide.description,\n      type: \"article\",\n      url: absoluteUrl(guide.slug),\n      images: [\n        {\n          url: ogUrl.toString(),\n          width: 1200,\n          height: 630,\n          alt: guide.title,\n        },\n      ],\n    },\n    twitter: {\n      card: \"summary_large_image\",\n      title: guide.title,\n      description: guide.description,\n      images: [ogUrl.toString()],\n    },\n  }\n}\n\nexport async function generateStaticParams(): Promise<\n  GuidePageProps[\"params\"][]\n> {\n  return allGuides.map((guide) => ({\n    slug: guide.slugAsParams.split(\"/\"),\n  }))\n}\n\nexport default async function GuidePage({ params }: GuidePageProps) {\n  const guide = await getGuideFromParams(params)\n\n  if (!guide) {\n    notFound()\n  }\n\n  const toc = await getTableOfContents(guide.body.raw)\n\n  return (\n    <main className=\"relative py-6 lg:grid lg:grid-cols-[1fr_300px] lg:gap-10 lg:py-10 xl:gap-20\">\n      <div>\n        <DocsPageHeader heading={guide.title} text={guide.description} />\n        <Mdx code={guide.body.code} />\n        <hr className=\"my-4\" />\n        <div className=\"flex justify-center py-6 lg:py-10\">\n          <Link\n            href=\"/guides\"\n            className={cn(buttonVariants({ variant: \"ghost\" }))}\n          >\n            <Icons.chevronLeft className=\"mr-2 h-4 w-4\" />\n            See all guides\n          </Link>\n        </div>\n      </div>\n      <div className=\"hidden text-sm lg:block\">\n        <div className=\"sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-10\">\n          <DashboardTableOfContents toc={toc} />\n        </div>\n      </div>\n    </main>\n  )\n}\n"
  },
  {
    "path": "app/(docs)/guides/layout.tsx",
    "content": "interface GuidesLayoutProps {\n  children: React.ReactNode\n}\n\nexport default function GuidesLayout({ children }: GuidesLayoutProps) {\n  return <div className=\"mx-auto max-w-5xl\">{children}</div>\n}\n"
  },
  {
    "path": "app/(docs)/guides/page.tsx",
    "content": "import Link from \"next/link\"\nimport { allGuides } from \"contentlayer/generated\"\nimport { compareDesc } from \"date-fns\"\n\nimport { formatDate } from \"@/lib/utils\"\nimport { DocsPageHeader } from \"@/components/page-header\"\n\nexport const metadata = {\n  title: \"Guides\",\n  description:\n    \"This section includes end-to-end guides for developing Next.js 13 apps.\",\n}\n\nexport default function GuidesPage() {\n  const guides = allGuides\n    .filter((guide) => guide.published)\n    .sort((a, b) => {\n      return compareDesc(new Date(a.date), new Date(b.date))\n    })\n\n  return (\n    <div className=\"py-6 lg:py-10\">\n      <DocsPageHeader\n        heading=\"Guides\"\n        text=\"This section includes end-to-end guides for developing Next.js 13 apps.\"\n      />\n      {guides?.length ? (\n        <div className=\"grid gap-4 md:grid-cols-2 md:gap-6\">\n          {guides.map((guide) => (\n            <article\n              key={guide._id}\n              className=\"group relative rounded-lg border p-6 shadow-md transition-shadow hover:shadow-lg\"\n            >\n              {guide.featured && (\n                <span className=\"absolute right-4 top-4 rounded-full px-3 py-1 text-xs font-medium\">\n                  Featured\n                </span>\n              )}\n              <div className=\"flex flex-col justify-between space-y-4\">\n                <div className=\"space-y-2\">\n                  <h2 className=\"text-xl font-medium tracking-tight\">\n                    {guide.title}\n                  </h2>\n                  {guide.description && (\n                    <p className=\"text-muted-foreground\">{guide.description}</p>\n                  )}\n                </div>\n                {guide.date && (\n                  <p className=\"text-sm text-muted-foreground\">\n                    {formatDate(guide.date)}\n                  </p>\n                )}\n              </div>\n              <Link href={guide.slug} className=\"absolute inset-0\">\n                <span className=\"sr-only\">View</span>\n              </Link>\n            </article>\n          ))}\n        </div>\n      ) : (\n        <p>No guides published.</p>\n      )}\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(docs)/layout.tsx",
    "content": "import Link from \"next/link\"\n\nimport { docsConfig } from \"@/config/docs\"\nimport { siteConfig } from \"@/config/site\"\nimport { Icons } from \"@/components/icons\"\nimport { MainNav } from \"@/components/main-nav\"\nimport { DocsSearch } from \"@/components/search\"\nimport { DocsSidebarNav } from \"@/components/sidebar-nav\"\nimport { SiteFooter } from \"@/components/site-footer\"\n\ninterface DocsLayoutProps {\n  children: React.ReactNode\n}\n\nexport default function DocsLayout({ children }: DocsLayoutProps) {\n  return (\n    <div className=\"flex min-h-screen flex-col\">\n      <header className=\"sticky top-0 z-40 w-full border-b bg-background\">\n        <div className=\"container flex h-16 items-center space-x-4 sm:justify-between sm:space-x-0\">\n          <MainNav items={docsConfig.mainNav}>\n            <DocsSidebarNav items={docsConfig.sidebarNav} />\n          </MainNav>\n          <div className=\"flex flex-1 items-center space-x-4 sm:justify-end\">\n            <div className=\"flex-1 sm:grow-0\">\n              <DocsSearch />\n            </div>\n            <nav className=\"flex space-x-4\">\n              <Link\n                href={siteConfig.links.github}\n                target=\"_blank\"\n                rel=\"noreferrer\"\n              >\n                <Icons.gitHub className=\"h-7 w-7\" />\n                <span className=\"sr-only\">GitHub</span>\n              </Link>\n            </nav>\n          </div>\n        </div>\n      </header>\n      <div className=\"container flex-1\">{children}</div>\n      <SiteFooter className=\"border-t\" />\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(editor)/editor/[postId]/loading.tsx",
    "content": "import { Skeleton } from \"@/components/ui/skeleton\"\n\nexport default function Loading() {\n  return (\n    <div className=\"grid w-full gap-10\">\n      <div className=\"flex w-full items-center justify-between\">\n        <Skeleton className=\"h-[38px] w-[90px]\" />\n        <Skeleton className=\"h-[38px] w-[80px]\" />\n      </div>\n      <div className=\"mx-auto w-[800px] space-y-6\">\n        <Skeleton className=\"h-[50px] w-full\" />\n        <Skeleton className=\"h-[20px] w-2/3\" />\n        <Skeleton className=\"h-[20px] w-full\" />\n        <Skeleton className=\"h-[20px] w-full\" />\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(editor)/editor/[postId]/not-found.tsx",
    "content": "import Link from \"next/link\"\n\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { EmptyPlaceholder } from \"@/components/empty-placeholder\"\n\nexport default function NotFound() {\n  return (\n    <EmptyPlaceholder className=\"mx-auto max-w-[800px]\">\n      <EmptyPlaceholder.Icon name=\"warning\" />\n      <EmptyPlaceholder.Title>Uh oh! Not Found</EmptyPlaceholder.Title>\n      <EmptyPlaceholder.Description>\n        This post cound not be found. Please try again.\n      </EmptyPlaceholder.Description>\n      <Link href=\"/dashboard\" className={buttonVariants({ variant: \"ghost\" })}>\n        Go to Dashboard\n      </Link>\n    </EmptyPlaceholder>\n  )\n}\n"
  },
  {
    "path": "app/(editor)/editor/[postId]/page.tsx",
    "content": "import { notFound, redirect } from \"next/navigation\"\nimport { Post, User } from \"@prisma/client\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { db } from \"@/lib/db\"\nimport { getCurrentUser } from \"@/lib/session\"\nimport { Editor } from \"@/components/editor\"\n\nasync function getPostForUser(postId: Post[\"id\"], userId: User[\"id\"]) {\n  return await db.post.findFirst({\n    where: {\n      id: postId,\n      authorId: userId,\n    },\n  })\n}\n\ninterface EditorPageProps {\n  params: { postId: string }\n}\n\nexport default async function EditorPage({ params }: EditorPageProps) {\n  const user = await getCurrentUser()\n\n  if (!user) {\n    redirect(authOptions?.pages?.signIn || \"/login\")\n  }\n\n  const post = await getPostForUser(params.postId, user.id)\n\n  if (!post) {\n    notFound()\n  }\n\n  return (\n    <Editor\n      post={{\n        id: post.id,\n        title: post.title,\n        content: post.content,\n        published: post.published,\n      }}\n    />\n  )\n}\n"
  },
  {
    "path": "app/(editor)/editor/layout.tsx",
    "content": "interface EditorProps {\n  children?: React.ReactNode\n}\n\nexport default function EditorLayout({ children }: EditorProps) {\n  return (\n    <div className=\"container mx-auto grid items-start gap-10 py-8\">\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(marketing)/[...slug]/page.tsx",
    "content": "import { notFound } from \"next/navigation\"\nimport { allPages } from \"contentlayer/generated\"\n\nimport { Mdx } from \"@/components/mdx-components\"\n\nimport \"@/styles/mdx.css\"\nimport { Metadata } from \"next\"\n\nimport { env } from \"@/env.mjs\"\nimport { siteConfig } from \"@/config/site\"\nimport { absoluteUrl } from \"@/lib/utils\"\n\ninterface PageProps {\n  params: {\n    slug: string[]\n  }\n}\n\nasync function getPageFromParams(params) {\n  const slug = params?.slug?.join(\"/\")\n  const page = allPages.find((page) => page.slugAsParams === slug)\n\n  if (!page) {\n    null\n  }\n\n  return page\n}\n\nexport async function generateMetadata({\n  params,\n}: PageProps): Promise<Metadata> {\n  const page = await getPageFromParams(params)\n\n  if (!page) {\n    return {}\n  }\n\n  const url = env.NEXT_PUBLIC_APP_URL\n\n  const ogUrl = new URL(`${url}/api/og`)\n  ogUrl.searchParams.set(\"heading\", page.title)\n  ogUrl.searchParams.set(\"type\", siteConfig.name)\n  ogUrl.searchParams.set(\"mode\", \"light\")\n\n  return {\n    title: page.title,\n    description: page.description,\n    openGraph: {\n      title: page.title,\n      description: page.description,\n      type: \"article\",\n      url: absoluteUrl(page.slug),\n      images: [\n        {\n          url: ogUrl.toString(),\n          width: 1200,\n          height: 630,\n          alt: page.title,\n        },\n      ],\n    },\n    twitter: {\n      card: \"summary_large_image\",\n      title: page.title,\n      description: page.description,\n      images: [ogUrl.toString()],\n    },\n  }\n}\n\nexport async function generateStaticParams(): Promise<PageProps[\"params\"][]> {\n  return allPages.map((page) => ({\n    slug: page.slugAsParams.split(\"/\"),\n  }))\n}\n\nexport default async function PagePage({ params }: PageProps) {\n  const page = await getPageFromParams(params)\n\n  if (!page) {\n    notFound()\n  }\n\n  return (\n    <article className=\"container max-w-3xl py-6 lg:py-12\">\n      <div className=\"space-y-4\">\n        <h1 className=\"inline-block font-heading text-4xl lg:text-5xl\">\n          {page.title}\n        </h1>\n        {page.description && (\n          <p className=\"text-xl text-muted-foreground\">{page.description}</p>\n        )}\n      </div>\n      <hr className=\"my-4\" />\n      <Mdx code={page.body.code} />\n    </article>\n  )\n}\n"
  },
  {
    "path": "app/(marketing)/blog/[...slug]/page.tsx",
    "content": "import { notFound } from \"next/navigation\"\nimport { allAuthors, allPosts } from \"contentlayer/generated\"\n\nimport { Mdx } from \"@/components/mdx-components\"\n\nimport \"@/styles/mdx.css\"\nimport { Metadata } from \"next\"\nimport Image from \"next/image\"\nimport Link from \"next/link\"\n\nimport { env } from \"@/env.mjs\"\nimport { absoluteUrl, cn, formatDate } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Icons } from \"@/components/icons\"\n\ninterface PostPageProps {\n  params: {\n    slug: string[]\n  }\n}\n\nasync function getPostFromParams(params) {\n  const slug = params?.slug?.join(\"/\")\n  const post = allPosts.find((post) => post.slugAsParams === slug)\n\n  if (!post) {\n    null\n  }\n\n  return post\n}\n\nexport async function generateMetadata({\n  params,\n}: PostPageProps): Promise<Metadata> {\n  const post = await getPostFromParams(params)\n\n  if (!post) {\n    return {}\n  }\n\n  const url = env.NEXT_PUBLIC_APP_URL\n\n  const ogUrl = new URL(`${url}/api/og`)\n  ogUrl.searchParams.set(\"heading\", post.title)\n  ogUrl.searchParams.set(\"type\", \"Blog Post\")\n  ogUrl.searchParams.set(\"mode\", \"dark\")\n\n  return {\n    title: post.title,\n    description: post.description,\n    authors: post.authors.map((author) => ({\n      name: author,\n    })),\n    openGraph: {\n      title: post.title,\n      description: post.description,\n      type: \"article\",\n      url: absoluteUrl(post.slug),\n      images: [\n        {\n          url: ogUrl.toString(),\n          width: 1200,\n          height: 630,\n          alt: post.title,\n        },\n      ],\n    },\n    twitter: {\n      card: \"summary_large_image\",\n      title: post.title,\n      description: post.description,\n      images: [ogUrl.toString()],\n    },\n  }\n}\n\nexport async function generateStaticParams(): Promise<\n  PostPageProps[\"params\"][]\n> {\n  return allPosts.map((post) => ({\n    slug: post.slugAsParams.split(\"/\"),\n  }))\n}\n\nexport default async function PostPage({ params }: PostPageProps) {\n  const post = await getPostFromParams(params)\n\n  if (!post) {\n    notFound()\n  }\n\n  const authors = post.authors.map((author) =>\n    allAuthors.find(({ slug }) => slug === `/authors/${author}`)\n  )\n\n  return (\n    <article className=\"container relative max-w-3xl py-6 lg:py-10\">\n      <Link\n        href=\"/blog\"\n        className={cn(\n          buttonVariants({ variant: \"ghost\" }),\n          \"absolute left-[-200px] top-14 hidden xl:inline-flex\"\n        )}\n      >\n        <Icons.chevronLeft className=\"mr-2 h-4 w-4\" />\n        See all posts\n      </Link>\n      <div>\n        {post.date && (\n          <time\n            dateTime={post.date}\n            className=\"block text-sm text-muted-foreground\"\n          >\n            Published on {formatDate(post.date)}\n          </time>\n        )}\n        <h1 className=\"mt-2 inline-block font-heading text-4xl leading-tight lg:text-5xl\">\n          {post.title}\n        </h1>\n        {authors?.length ? (\n          <div className=\"mt-4 flex space-x-4\">\n            {authors.map((author) =>\n              author ? (\n                <Link\n                  key={author._id}\n                  href={`https://twitter.com/${author.twitter}`}\n                  className=\"flex items-center space-x-2 text-sm\"\n                >\n                  <Image\n                    src={author.avatar}\n                    alt={author.title}\n                    width={42}\n                    height={42}\n                    className=\"rounded-full bg-white\"\n                  />\n                  <div className=\"flex-1 text-left leading-tight\">\n                    <p className=\"font-medium\">{author.title}</p>\n                    <p className=\"text-[12px] text-muted-foreground\">\n                      @{author.twitter}\n                    </p>\n                  </div>\n                </Link>\n              ) : null\n            )}\n          </div>\n        ) : null}\n      </div>\n      {post.image && (\n        <Image\n          src={post.image}\n          alt={post.title}\n          width={720}\n          height={405}\n          className=\"my-8 rounded-md border bg-muted transition-colors\"\n          priority\n        />\n      )}\n      <Mdx code={post.body.code} />\n      <hr className=\"mt-12\" />\n      <div className=\"flex justify-center py-6 lg:py-10\">\n        <Link href=\"/blog\" className={cn(buttonVariants({ variant: \"ghost\" }))}>\n          <Icons.chevronLeft className=\"mr-2 h-4 w-4\" />\n          See all posts\n        </Link>\n      </div>\n    </article>\n  )\n}\n"
  },
  {
    "path": "app/(marketing)/blog/page.tsx",
    "content": "import Image from \"next/image\"\nimport Link from \"next/link\"\nimport { allPosts } from \"contentlayer/generated\"\nimport { compareDesc } from \"date-fns\"\n\nimport { formatDate } from \"@/lib/utils\"\n\nexport const metadata = {\n  title: \"Blog\",\n}\n\nexport default async function BlogPage() {\n  const posts = allPosts\n    .filter((post) => post.published)\n    .sort((a, b) => {\n      return compareDesc(new Date(a.date), new Date(b.date))\n    })\n\n  return (\n    <div className=\"container max-w-4xl py-6 lg:py-10\">\n      <div className=\"flex flex-col items-start gap-4 md:flex-row md:justify-between md:gap-8\">\n        <div className=\"flex-1 space-y-4\">\n          <h1 className=\"inline-block font-heading text-4xl tracking-tight lg:text-5xl\">\n            Blog\n          </h1>\n          <p className=\"text-xl text-muted-foreground\">\n            A blog built using Contentlayer. Posts are written in MDX.\n          </p>\n        </div>\n      </div>\n      <hr className=\"my-8\" />\n      {posts?.length ? (\n        <div className=\"grid gap-10 sm:grid-cols-2\">\n          {posts.map((post, index) => (\n            <article\n              key={post._id}\n              className=\"group relative flex flex-col space-y-2\"\n            >\n              {post.image && (\n                <Image\n                  src={post.image}\n                  alt={post.title}\n                  width={804}\n                  height={452}\n                  className=\"rounded-md border bg-muted transition-colors\"\n                  priority={index <= 1}\n                />\n              )}\n              <h2 className=\"text-2xl font-extrabold\">{post.title}</h2>\n              {post.description && (\n                <p className=\"text-muted-foreground\">{post.description}</p>\n              )}\n              {post.date && (\n                <p className=\"text-sm text-muted-foreground\">\n                  {formatDate(post.date)}\n                </p>\n              )}\n              <Link href={post.slug} className=\"absolute inset-0\">\n                <span className=\"sr-only\">View Article</span>\n              </Link>\n            </article>\n          ))}\n        </div>\n      ) : (\n        <p>No posts published.</p>\n      )}\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(marketing)/layout.tsx",
    "content": "import Link from \"next/link\"\n\nimport { marketingConfig } from \"@/config/marketing\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { MainNav } from \"@/components/main-nav\"\nimport { SiteFooter } from \"@/components/site-footer\"\n\ninterface MarketingLayoutProps {\n  children: React.ReactNode\n}\n\nexport default async function MarketingLayout({\n  children,\n}: MarketingLayoutProps) {\n  return (\n    <div className=\"flex min-h-screen flex-col\">\n      <header className=\"container z-40 bg-background\">\n        <div className=\"flex h-20 items-center justify-between py-6\">\n          <MainNav items={marketingConfig.mainNav} />\n          <nav>\n            <Link\n              href=\"/login\"\n              className={cn(\n                buttonVariants({ variant: \"secondary\", size: \"sm\" }),\n                \"px-4\"\n              )}\n            >\n              Login\n            </Link>\n          </nav>\n        </div>\n      </header>\n      <main className=\"flex-1\">{children}</main>\n      <SiteFooter />\n    </div>\n  )\n}\n"
  },
  {
    "path": "app/(marketing)/page.tsx",
    "content": "import Link from \"next/link\"\n\nimport { env } from \"@/env.mjs\"\nimport { siteConfig } from \"@/config/site\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\n\nasync function getGitHubStars(): Promise<string | null> {\n  try {\n    const response = await fetch(\n      \"https://api.github.com/repos/shadcn/taxonomy\",\n      {\n        headers: {\n          Accept: \"application/vnd.github+json\",\n          Authorization: `Bearer ${env.GITHUB_ACCESS_TOKEN}`,\n        },\n        next: {\n          revalidate: 60,\n        },\n      }\n    )\n\n    if (!response?.ok) {\n      return null\n    }\n\n    const json = await response.json()\n\n    return parseInt(json[\"stargazers_count\"]).toLocaleString()\n  } catch (error) {\n    return null\n  }\n}\n\nexport default async function IndexPage() {\n  const stars = await getGitHubStars()\n\n  return (\n    <>\n      <section className=\"space-y-6 pb-8 pt-6 md:pb-12 md:pt-10 lg:py-32\">\n        <div className=\"container flex max-w-[64rem] flex-col items-center gap-4 text-center\">\n          <Link\n            href={siteConfig.links.twitter}\n            className=\"rounded-2xl bg-muted px-4 py-1.5 text-sm font-medium\"\n            target=\"_blank\"\n          >\n            Follow along on Twitter\n          </Link>\n          <h1 className=\"font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl\">\n            An example app built using Next.js 13 server components.\n          </h1>\n          <p className=\"max-w-[42rem] leading-normal text-muted-foreground sm:text-xl sm:leading-8\">\n            I&apos;m building a web app with Next.js 13 and open sourcing\n            everything. Follow along as we figure this out together.\n          </p>\n          <div className=\"space-x-4\">\n            <Link href=\"/login\" className={cn(buttonVariants({ size: \"lg\" }))}>\n              Get Started\n            </Link>\n            <Link\n              href={siteConfig.links.github}\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className={cn(buttonVariants({ variant: \"outline\", size: \"lg\" }))}\n            >\n              GitHub\n            </Link>\n          </div>\n        </div>\n      </section>\n      <section\n        id=\"features\"\n        className=\"container space-y-6 bg-slate-50 py-8 dark:bg-transparent md:py-12 lg:py-24\"\n      >\n        <div className=\"mx-auto flex max-w-[58rem] flex-col items-center space-y-4 text-center\">\n          <h2 className=\"font-heading text-3xl leading-[1.1] sm:text-3xl md:text-6xl\">\n            Features\n          </h2>\n          <p className=\"max-w-[85%] leading-normal text-muted-foreground sm:text-lg sm:leading-7\">\n            This project is an experiment to see how a modern app, with features\n            like auth, subscriptions, API routes, and static pages would work in\n            Next.js 13 app dir.\n          </p>\n        </div>\n        <div className=\"mx-auto grid justify-center gap-4 sm:grid-cols-2 md:max-w-[64rem] md:grid-cols-3\">\n          <div className=\"relative overflow-hidden rounded-lg border bg-background p-2\">\n            <div className=\"flex h-[180px] flex-col justify-between rounded-md p-6\">\n              <svg viewBox=\"0 0 24 24\" className=\"h-12 w-12 fill-current\">\n                <path d=\"M11.572 0c-.176 0-.31.001-.358.007a19.76 19.76 0 0 1-.364.033C7.443.346 4.25 2.185 2.228 5.012a11.875 11.875 0 0 0-2.119 5.243c-.096.659-.108.854-.108 1.747s.012 1.089.108 1.748c.652 4.506 3.86 8.292 8.209 9.695.779.25 1.6.422 2.534.525.363.04 1.935.04 2.299 0 1.611-.178 2.977-.577 4.323-1.264.207-.106.247-.134.219-.158-.02-.013-.9-1.193-1.955-2.62l-1.919-2.592-2.404-3.558a338.739 338.739 0 0 0-2.422-3.556c-.009-.002-.018 1.579-.023 3.51-.007 3.38-.01 3.515-.052 3.595a.426.426 0 0 1-.206.214c-.075.037-.14.044-.495.044H7.81l-.108-.068a.438.438 0 0 1-.157-.171l-.05-.106.006-4.703.007-4.705.072-.092a.645.645 0 0 1 .174-.143c.096-.047.134-.051.54-.051.478 0 .558.018.682.154.035.038 1.337 1.999 2.895 4.361a10760.433 10760.433 0 0 0 4.735 7.17l1.9 2.879.096-.063a12.317 12.317 0 0 0 2.466-2.163 11.944 11.944 0 0 0 2.824-6.134c.096-.66.108-.854.108-1.748 0-.893-.012-1.088-.108-1.747-.652-4.506-3.859-8.292-8.208-9.695a12.597 12.597 0 0 0-2.499-.523A33.119 33.119 0 0 0 11.573 0zm4.069 7.217c.347 0 .408.005.486.047a.473.473 0 0 1 .237.277c.018.06.023 1.365.018 4.304l-.006 4.218-.744-1.14-.746-1.14v-3.066c0-1.982.01-3.097.023-3.15a.478.478 0 0 1 .233-.296c.096-.05.13-.054.5-.054z\" />\n              </svg>\n              <div className=\"space-y-2\">\n                <h3 className=\"font-bold\">Next.js 13</h3>\n                <p className=\"text-sm text-muted-foreground\">\n                  App dir, Routing, Layouts, Loading UI and API routes.\n                </p>\n              </div>\n            </div>\n          </div>\n          <div className=\"relative overflow-hidden rounded-lg border bg-background p-2\">\n            <div className=\"flex h-[180px] flex-col justify-between rounded-md p-6\">\n              <svg viewBox=\"0 0 24 24\" className=\"h-12 w-12 fill-current\">\n                <path d=\"M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38a2.167 2.167 0 0 0-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44a23.476 23.476 0 0 0-3.107-.534A23.892 23.892 0 0 0 12.769 4.7c1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442a22.73 22.73 0 0 0-3.113.538 15.02 15.02 0 0 1-.254-1.42c-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87a25.64 25.64 0 0 1-4.412.005 26.64 26.64 0 0 1-1.183-1.86c-.372-.64-.71-1.29-1.018-1.946a25.17 25.17 0 0 1 1.013-1.954c.38-.66.773-1.286 1.18-1.868A25.245 25.245 0 0 1 12 8.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933a25.952 25.952 0 0 0-1.345-2.32zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493a23.966 23.966 0 0 0-1.1-2.98c.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98a23.142 23.142 0 0 0-1.086 2.964c-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39a25.819 25.819 0 0 0 1.341-2.338zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143a22.005 22.005 0 0 1-2.006-.386c.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295a1.185 1.185 0 0 1-.553-.132c-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z\" />\n              </svg>\n              <div className=\"space-y-2\">\n                <h3 className=\"font-bold\">React 18</h3>\n                <p className=\"text-sm\">\n                  Server and Client Components. Use hook.\n                </p>\n              </div>\n            </div>\n          </div>\n          <div className=\"relative overflow-hidden rounded-lg border bg-background p-2\">\n            <div className=\"flex h-[180px] flex-col justify-between rounded-md p-6\">\n              <svg viewBox=\"0 0 24 24\" className=\"h-12 w-12 fill-current\">\n                <path d=\"M0 12C0 5.373 5.373 0 12 0c4.873 0 9.067 2.904 10.947 7.077l-15.87 15.87a11.981 11.981 0 0 1-1.935-1.099L14.99 12H12l-8.485 8.485A11.962 11.962 0 0 1 0 12Zm12.004 12L24 12.004C23.998 18.628 18.628 23.998 12.004 24Z\" />\n              </svg>\n              <div className=\"space-y-2\">\n                <h3 className=\"font-bold\">Database</h3>\n                <p className=\"text-sm text-muted-foreground\">\n                  ORM using Prisma and deployed on PlanetScale.\n                </p>\n              </div>\n            </div>\n          </div>\n          <div className=\"relative overflow-hidden rounded-lg border bg-background p-2\">\n            <div className=\"flex h-[180px] flex-col justify-between rounded-md p-6\">\n              <svg viewBox=\"0 0 24 24\" className=\"h-12 w-12 fill-current\">\n                <path d=\"M12.001 4.8c-3.2 0-5.2 1.6-6 4.8 1.2-1.6 2.6-2.2 4.2-1.8.913.228 1.565.89 2.288 1.624C13.666 10.618 15.027 12 18.001 12c3.2 0 5.2-1.6 6-4.8-1.2 1.6-2.6 2.2-4.2 1.8-.913-.228-1.565-.89-2.288-1.624C16.337 6.182 14.976 4.8 12.001 4.8zm-6 7.2c-3.2 0-5.2 1.6-6 4.8 1.2-1.6 2.6-2.2 4.2-1.8.913.228 1.565.89 2.288 1.624 1.177 1.194 2.538 2.576 5.512 2.576 3.2 0 5.2-1.6 6-4.8-1.2 1.6-2.6 2.2-4.2 1.8-.913-.228-1.565-.89-2.288-1.624C10.337 13.382 8.976 12 6.001 12z\" />\n              </svg>\n              <div className=\"space-y-2\">\n                <h3 className=\"font-bold\">Components</h3>\n                <p className=\"text-sm text-muted-foreground\">\n                  UI components built using Radix UI and styled with Tailwind\n                  CSS.\n                </p>\n              </div>\n            </div>\n          </div>\n          <div className=\"relative overflow-hidden rounded-lg border bg-background p-2\">\n            <div className=\"flex h-[180px] flex-col justify-between rounded-md p-6\">\n              <svg\n                viewBox=\"0 0 24 24\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1\"\n                className=\"h-12 w-12 fill-current\"\n              >\n                <path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"></path>\n              </svg>\n              <div className=\"space-y-2\">\n                <h3 className=\"font-bold\">Authentication</h3>\n                <p className=\"text-sm text-muted-foreground\">\n                  Authentication using NextAuth.js and middlewares.\n                </p>\n              </div>\n            </div>\n          </div>\n          <div className=\"relative overflow-hidden rounded-lg border bg-background p-2\">\n            <div className=\"flex h-[180px] flex-col justify-between rounded-md p-6\">\n              <svg viewBox=\"0 0 24 24\" className=\"h-12 w-12 fill-current\">\n                <path d=\"M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z\" />\n              </svg>\n              <div className=\"space-y-2\">\n                <h3 className=\"font-bold\">Subscriptions</h3>\n                <p className=\"text-sm text-muted-foreground\">\n                  Free and paid subscriptions using Stripe.\n                </p>\n              </div>\n            </div>\n          </div>\n        </div>\n        <div className=\"mx-auto text-center md:max-w-[58rem]\">\n          <p className=\"leading-normal text-muted-foreground sm:text-lg sm:leading-7\">\n            Taxonomy also includes a blog and a full-featured documentation site\n            built using Contentlayer and MDX.\n          </p>\n        </div>\n      </section>\n      <section id=\"open-source\" className=\"container py-8 md:py-12 lg:py-24\">\n        <div className=\"mx-auto flex max-w-[58rem] flex-col items-center justify-center gap-4 text-center\">\n          <h2 className=\"font-heading text-3xl leading-[1.1] sm:text-3xl md:text-6xl\">\n            Proudly Open Source\n          </h2>\n          <p className=\"max-w-[85%] leading-normal text-muted-foreground sm:text-lg sm:leading-7\">\n            Taxonomy is open source and powered by open source software. <br />{\" \"}\n            The code is available on{\" \"}\n            <Link\n              href={siteConfig.links.github}\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"underline underline-offset-4\"\n            >\n              GitHub\n            </Link>\n            .{\" \"}\n          </p>\n          {stars && (\n            <Link\n              href={siteConfig.links.github}\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"flex\"\n            >\n              <div className=\"flex h-10 w-10 items-center justify-center space-x-2 rounded-md border border-muted bg-muted\">\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  fill=\"currentColor\"\n                  viewBox=\"0 0 24 24\"\n                  className=\"h-5 w-5 text-foreground\"\n                >\n                  <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\"></path>\n                </svg>\n              </div>\n              <div className=\"flex items-center\">\n                <div className=\"h-4 w-4 border-y-8 border-l-0 border-r-8 border-solid border-muted border-y-transparent\"></div>\n                <div className=\"flex h-10 items-center rounded-md border border-muted bg-muted px-4 font-medium\">\n                  {stars} stars on GitHub\n                </div>\n              </div>\n            </Link>\n          )}\n        </div>\n      </section>\n    </>\n  )\n}\n"
  },
  {
    "path": "app/(marketing)/pricing/page.tsx",
    "content": "import Link from \"next/link\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Icons } from \"@/components/icons\"\n\nexport const metadata = {\n  title: \"Pricing\",\n}\n\nexport default function PricingPage() {\n  return (\n    <section className=\"container flex flex-col  gap-6 py-8 md:max-w-[64rem] md:py-12 lg:py-24\">\n      <div className=\"mx-auto flex w-full flex-col gap-4 md:max-w-[58rem]\">\n        <h2 className=\"font-heading text-3xl leading-[1.1] sm:text-3xl md:text-6xl\">\n          Simple, transparent pricing\n        </h2>\n        <p className=\"max-w-[85%] leading-normal text-muted-foreground sm:text-lg sm:leading-7\">\n          Unlock all features including unlimited posts for your blog.\n        </p>\n      </div>\n      <div className=\"grid w-full items-start gap-10 rounded-lg border p-10 md:grid-cols-[1fr_200px]\">\n        <div className=\"grid gap-6\">\n          <h3 className=\"text-xl font-bold sm:text-2xl\">\n            What&apos;s included in the PRO plan\n          </h3>\n          <ul className=\"grid gap-3 text-sm text-muted-foreground sm:grid-cols-2\">\n            <li className=\"flex items-center\">\n              <Icons.check className=\"mr-2 h-4 w-4\" /> Unlimited Posts\n            </li>\n            <li className=\"flex items-center\">\n              <Icons.check className=\"mr-2 h-4 w-4\" /> Unlimited Users\n            </li>\n\n            <li className=\"flex items-center\">\n              <Icons.check className=\"mr-2 h-4 w-4\" /> Custom domain\n            </li>\n            <li className=\"flex items-center\">\n              <Icons.check className=\"mr-2 h-4 w-4\" /> Dashboard Analytics\n            </li>\n            <li className=\"flex items-center\">\n              <Icons.check className=\"mr-2 h-4 w-4\" /> Access to Discord\n            </li>\n            <li className=\"flex items-center\">\n              <Icons.check className=\"mr-2 h-4 w-4\" /> Premium Support\n            </li>\n          </ul>\n        </div>\n        <div className=\"flex flex-col gap-4 text-center\">\n          <div>\n            <h4 className=\"text-7xl font-bold\">$19</h4>\n            <p className=\"text-sm font-medium text-muted-foreground\">\n              Billed Monthly\n            </p>\n          </div>\n          <Link href=\"/login\" className={cn(buttonVariants({ size: \"lg\" }))}>\n            Get Started\n          </Link>\n        </div>\n      </div>\n      <div className=\"mx-auto flex w-full max-w-[58rem] flex-col gap-4\">\n        <p className=\"max-w-[85%] leading-normal text-muted-foreground sm:leading-7\">\n          Taxonomy is a demo app.{\" \"}\n          <strong>You can test the upgrade and won&apos;t be charged.</strong>\n        </p>\n      </div>\n    </section>\n  )\n}\n"
  },
  {
    "path": "app/api/auth/[...nextauth]/_route.ts",
    "content": "import NextAuth from \"next-auth\"\n\nimport { authOptions } from \"@/lib/auth\"\n\nconst handler = NextAuth(authOptions)\n\nexport { handler as GET, handler as POST }\n"
  },
  {
    "path": "app/api/og/route.tsx",
    "content": "import { ImageResponse } from \"@vercel/og\"\n\nimport { ogImageSchema } from \"@/lib/validations/og\"\n\nexport const runtime = \"edge\"\n\nconst interRegular = fetch(\n  new URL(\"../../../assets/fonts/Inter-Regular.ttf\", import.meta.url)\n).then((res) => res.arrayBuffer())\n\nconst interBold = fetch(\n  new URL(\"../../../assets/fonts/CalSans-SemiBold.ttf\", import.meta.url)\n).then((res) => res.arrayBuffer())\n\nexport async function GET(req: Request) {\n  try {\n    const fontRegular = await interRegular\n    const fontBold = await interBold\n\n    const url = new URL(req.url)\n    const values = ogImageSchema.parse(Object.fromEntries(url.searchParams))\n    const heading =\n      values.heading.length > 140\n        ? `${values.heading.substring(0, 140)}...`\n        : values.heading\n\n    const { mode } = values\n    const paint = mode === \"dark\" ? \"#fff\" : \"#000\"\n\n    const fontSize = heading.length > 100 ? \"70px\" : \"100px\"\n\n    return new ImageResponse(\n      (\n        <div\n          tw=\"flex relative flex-col p-12 w-full h-full items-start\"\n          style={{\n            color: paint,\n            background:\n              mode === \"dark\"\n                ? \"linear-gradient(90deg, #000 0%, #111 100%)\"\n                : \"white\",\n          }}\n        >\n          <svg width=\"212\" height=\"50\" viewBox=\"0 0 212 50\" fill=\"none\">\n            <g clip-path=\"url(#a)\" fill={paint}>\n              <path d=\"M99.715 9.784h26.128v4.823h-10.365v25.37h-5.182v-25.37h-10.58V9.784ZM56.746 9.784v4.823H35.803v7.757h16.842v4.823H35.803v7.967h20.943v4.823H30.62v-25.37h-.002V9.784h26.128ZM69.792 9.797H63.01l24.292 30.192h6.801L81.956 24.903 94.084 9.82l-6.782.01-8.742 10.856-8.768-10.89ZM76.751 31.363l-3.396-4.222L62.99 40.012h6.802l6.96-8.649Z\" />\n              <path\n                fill-rule=\"evenodd\"\n                clip-rule=\"evenodd\"\n                d=\"M30.802 39.977 6.478 9.77H0v30.193h5.182V16.225l19.11 23.752h6.51Z\"\n              />\n              <path d=\"M127.008 39.792c-.38 0-.703-.131-.973-.394a1.267 1.267 0 0 1-.4-.959c-.004-.366.13-.681.4-.944.27-.263.593-.395.973-.395.365 0 .684.132.955.395.274.263.41.578.414.944-.004.25-.067.478-.193.682-.13.208-.295.37-.502.488a1.298 1.298 0 0 1-.674.183ZM135.853 27.073h2.296v8.847c-.003.814-.179 1.51-.523 2.094a3.477 3.477 0 0 1-1.447 1.346c-.614.311-1.334.47-2.152.47-.748 0-1.419-.135-2.016-.398a3.239 3.239 0 0 1-1.418-1.176c-.352-.519-.524-1.166-.524-1.941h2.301c.003.339.08.633.228.879.147.245.351.432.611.564.263.131.565.197.905.197.369 0 .685-.076.942-.232.256-.152.453-.38.59-.685.133-.301.203-.675.207-1.118v-8.847ZM147.598 30.533a1.67 1.67 0 0 0-.73-1.252c-.432-.301-.99-.45-1.675-.45-.481 0-.895.073-1.239.214-.345.146-.611.34-.794.585a1.423 1.423 0 0 0-.281.84c0 .264.063.492.186.683.123.193.288.356.502.487.211.135.446.246.703.336.259.09.519.166.779.228l1.197.294c.481.111.949.26 1.394.45.446.187.85.426 1.205.713.354.287.635.633.842 1.038.208.405.313.879.313 1.426 0 .737-.19 1.384-.573 1.944-.382.557-.933.993-1.657 1.308-.72.312-1.59.47-2.616.47-.99 0-1.854-.151-2.581-.456-.73-.301-1.299-.744-1.71-1.325-.41-.582-.632-1.29-.663-2.125h2.275c.032.436.172.8.411 1.094.242.29.558.505.945.65.389.142.825.215 1.306.215.502 0 .944-.076 1.327-.225.379-.149.678-.357.892-.626.218-.267.327-.582.33-.942-.003-.328-.102-.602-.292-.816-.193-.215-.459-.395-.8-.54a8.25 8.25 0 0 0-1.201-.39l-1.454-.368c-1.05-.266-1.882-.671-2.489-1.214-.611-.543-.913-1.263-.913-2.166 0-.74.203-1.391.615-1.948.407-.557.965-.99 1.671-1.298.709-.311 1.51-.463 2.401-.463.906 0 1.7.152 2.385.463.684.308 1.222.737 1.611 1.284a3.25 3.25 0 0 1 .605 1.882h-2.227Z\" />\n            </g>\n            <path\n              d=\"M181.335 14.636V35h-5.528V19.727h-.119l-4.455 2.665v-4.693l5.011-3.063h5.091Zm12.136 20.642c-1.604 0-3.029-.275-4.276-.825-1.239-.557-2.214-1.322-2.923-2.297-.709-.974-1.067-2.094-1.074-3.36h5.568c.007.39.126.742.358 1.053.239.305.564.544.975.716.411.173.881.259 1.412.259.51 0 .961-.09 1.352-.269.391-.185.696-.44.915-.765.218-.325.325-.696.318-1.114a1.637 1.637 0 0 0-.378-1.094c-.252-.318-.606-.566-1.064-.745-.457-.18-.984-.269-1.581-.269h-2.068V22.75h2.068c.55 0 1.034-.09 1.452-.268.424-.18.752-.428.984-.746.239-.318.355-.683.348-1.094a1.824 1.824 0 0 0-.288-1.054 2.012 2.012 0 0 0-.835-.716c-.352-.172-.759-.258-1.223-.258-.504 0-.955.09-1.353.268a2.25 2.25 0 0 0-.924.746 1.891 1.891 0 0 0-.348 1.094h-5.29c.007-1.247.348-2.347 1.024-3.302.683-.954 1.617-1.703 2.804-2.247 1.187-.543 2.549-.815 4.087-.815 1.504 0 2.833.255 3.987.766 1.16.51 2.065 1.213 2.714 2.107.657.889.981 1.906.975 3.053.013 1.14-.378 2.075-1.174 2.804-.788.73-1.789 1.16-3.002 1.293v.159c1.644.179 2.88.683 3.708 1.511.829.822 1.237 1.856 1.223 3.102.007 1.194-.351 2.25-1.073 3.172-.716.922-1.714 1.644-2.993 2.168-1.273.524-2.741.785-4.405.785Z\"\n              fill={paint}\n            />\n            <rect\n              x=\"163\"\n              y=\"1\"\n              width=\"48\"\n              height=\"48\"\n              rx=\"9\"\n              stroke={paint}\n              stroke-width=\"2\"\n            />\n            <defs>\n              <clipPath id=\"a\">\n                <path fill={paint} d=\"M0 9.771h150v30.457H0z\" />\n              </clipPath>\n            </defs>\n          </svg>\n          <div tw=\"flex flex-col flex-1 py-10\">\n            <div\n              tw=\"flex text-xl uppercase font-bold tracking-tight\"\n              style={{ fontFamily: \"Inter\", fontWeight: \"normal\" }}\n            >\n              {values.type}\n            </div>\n            <div\n              tw=\"flex leading-[1.1] text-[80px] font-bold\"\n              style={{\n                fontFamily: \"Cal Sans\",\n                fontWeight: \"bold\",\n                marginLeft: \"-3px\",\n                fontSize,\n              }}\n            >\n              {heading}\n            </div>\n          </div>\n          <div tw=\"flex items-center w-full justify-between\">\n            <div\n              tw=\"flex text-xl\"\n              style={{ fontFamily: \"Inter\", fontWeight: \"normal\" }}\n            >\n              tx.shadcn.com\n            </div>\n            <div\n              tw=\"flex items-center text-xl\"\n              style={{ fontFamily: \"Inter\", fontWeight: \"normal\" }}\n            >\n              <svg width=\"32\" height=\"32\" viewBox=\"0 0 48 48\" fill=\"none\">\n                <path\n                  d=\"M30 44v-8a9.6 9.6 0 0 0-2-7c6 0 12-4 12-11 .16-2.5-.54-4.96-2-7 .56-2.3.56-4.7 0-7 0 0-2 0-6 3-5.28-1-10.72-1-16 0-4-3-6-3-6-3-.6 2.3-.6 4.7 0 7a10.806 10.806 0 0 0-2 7c0 7 6 11 12 11a9.43 9.43 0 0 0-1.7 3.3c-.34 1.2-.44 2.46-.3 3.7v8\"\n                  stroke={paint}\n                  stroke-width=\"2\"\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                />\n                <path\n                  d=\"M18 36c-9.02 4-10-4-14-4\"\n                  stroke={paint}\n                  stroke-width=\"2\"\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                />\n              </svg>\n              <div tw=\"flex ml-2\">github.com/shadcn/taxonomy</div>\n            </div>\n          </div>\n        </div>\n      ),\n      {\n        width: 1200,\n        height: 630,\n        fonts: [\n          {\n            name: \"Inter\",\n            data: fontRegular,\n            weight: 400,\n            style: \"normal\",\n          },\n          {\n            name: \"Cal Sans\",\n            data: fontBold,\n            weight: 700,\n            style: \"normal\",\n          },\n        ],\n      }\n    )\n  } catch (error) {\n    return new Response(`Failed to generate image`, {\n      status: 500,\n    })\n  }\n}\n"
  },
  {
    "path": "app/api/posts/[postId]/route.ts",
    "content": "import { getServerSession } from \"next-auth\"\nimport * as z from \"zod\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { db } from \"@/lib/db\"\nimport { postPatchSchema } from \"@/lib/validations/post\"\n\nconst routeContextSchema = z.object({\n  params: z.object({\n    postId: z.string(),\n  }),\n})\n\nexport async function DELETE(\n  req: Request,\n  context: z.infer<typeof routeContextSchema>\n) {\n  try {\n    // Validate the route params.\n    const { params } = routeContextSchema.parse(context)\n\n    // Check if the user has access to this post.\n    if (!(await verifyCurrentUserHasAccessToPost(params.postId))) {\n      return new Response(null, { status: 403 })\n    }\n\n    // Delete the post.\n    await db.post.delete({\n      where: {\n        id: params.postId as string,\n      },\n    })\n\n    return new Response(null, { status: 204 })\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      return new Response(JSON.stringify(error.issues), { status: 422 })\n    }\n\n    return new Response(null, { status: 500 })\n  }\n}\n\nexport async function PATCH(\n  req: Request,\n  context: z.infer<typeof routeContextSchema>\n) {\n  try {\n    // Validate route params.\n    const { params } = routeContextSchema.parse(context)\n\n    // Check if the user has access to this post.\n    if (!(await verifyCurrentUserHasAccessToPost(params.postId))) {\n      return new Response(null, { status: 403 })\n    }\n\n    // Get the request body and validate it.\n    const json = await req.json()\n    const body = postPatchSchema.parse(json)\n\n    // Update the post.\n    // TODO: Implement sanitization for content.\n    await db.post.update({\n      where: {\n        id: params.postId,\n      },\n      data: {\n        title: body.title,\n        content: body.content,\n      },\n    })\n\n    return new Response(null, { status: 200 })\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      return new Response(JSON.stringify(error.issues), { status: 422 })\n    }\n\n    return new Response(null, { status: 500 })\n  }\n}\n\nasync function verifyCurrentUserHasAccessToPost(postId: string) {\n  const session = await getServerSession(authOptions)\n  const count = await db.post.count({\n    where: {\n      id: postId,\n      authorId: session?.user.id,\n    },\n  })\n\n  return count > 0\n}\n"
  },
  {
    "path": "app/api/posts/route.ts",
    "content": "import { getServerSession } from \"next-auth/next\"\nimport * as z from \"zod\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { db } from \"@/lib/db\"\nimport { RequiresProPlanError } from \"@/lib/exceptions\"\nimport { getUserSubscriptionPlan } from \"@/lib/subscription\"\n\nconst postCreateSchema = z.object({\n  title: z.string(),\n  content: z.string().optional(),\n})\n\nexport async function GET() {\n  try {\n    const session = await getServerSession(authOptions)\n\n    if (!session) {\n      return new Response(\"Unauthorized\", { status: 403 })\n    }\n\n    const { user } = session\n    const posts = await db.post.findMany({\n      select: {\n        id: true,\n        title: true,\n        published: true,\n        createdAt: true,\n      },\n      where: {\n        authorId: user.id,\n      },\n    })\n\n    return new Response(JSON.stringify(posts))\n  } catch (error) {\n    return new Response(null, { status: 500 })\n  }\n}\n\nexport async function POST(req: Request) {\n  try {\n    const session = await getServerSession(authOptions)\n\n    if (!session) {\n      return new Response(\"Unauthorized\", { status: 403 })\n    }\n\n    const { user } = session\n    const subscriptionPlan = await getUserSubscriptionPlan(user.id)\n\n    // If user is on a free plan.\n    // Check if user has reached limit of 3 posts.\n    if (!subscriptionPlan?.isPro) {\n      const count = await db.post.count({\n        where: {\n          authorId: user.id,\n        },\n      })\n\n      if (count >= 3) {\n        throw new RequiresProPlanError()\n      }\n    }\n\n    const json = await req.json()\n    const body = postCreateSchema.parse(json)\n\n    const post = await db.post.create({\n      data: {\n        title: body.title,\n        content: body.content,\n        authorId: session.user.id,\n      },\n      select: {\n        id: true,\n      },\n    })\n\n    return new Response(JSON.stringify(post))\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      return new Response(JSON.stringify(error.issues), { status: 422 })\n    }\n\n    if (error instanceof RequiresProPlanError) {\n      return new Response(\"Requires Pro Plan\", { status: 402 })\n    }\n\n    return new Response(null, { status: 500 })\n  }\n}\n"
  },
  {
    "path": "app/api/users/[userId]/route.ts",
    "content": "import { getServerSession } from \"next-auth/next\"\nimport { z } from \"zod\"\n\nimport { authOptions } from \"@/lib/auth\"\nimport { db } from \"@/lib/db\"\nimport { userNameSchema } from \"@/lib/validations/user\"\n\nconst routeContextSchema = z.object({\n  params: z.object({\n    userId: z.string(),\n  }),\n})\n\nexport async function PATCH(\n  req: Request,\n  context: z.infer<typeof routeContextSchema>\n) {\n  try {\n    // Validate the route context.\n    const { params } = routeContextSchema.parse(context)\n\n    // Ensure user is authentication and has access to this user.\n    const session = await getServerSession(authOptions)\n    if (!session?.user || params.userId !== session?.user.id) {\n      return new Response(null, { status: 403 })\n    }\n\n    // Get the request body and validate it.\n    const body = await req.json()\n    const payload = userNameSchema.parse(body)\n\n    // Update the user.\n    await db.user.update({\n      where: {\n        id: session.user.id,\n      },\n      data: {\n        name: payload.name,\n      },\n    })\n\n    return new Response(null, { status: 200 })\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      return new Response(JSON.stringify(error.issues), { status: 422 })\n    }\n\n    return new Response(null, { status: 500 })\n  }\n}\n"
  },
  {
    "path": "app/api/users/stripe/route.ts",
    "content": "import { getServerSession } from \"next-auth/next\"\nimport { z } from \"zod\"\n\nimport { proPlan } from \"@/config/subscriptions\"\nimport { authOptions } from \"@/lib/auth\"\nimport { stripe } from \"@/lib/stripe\"\nimport { getUserSubscriptionPlan } from \"@/lib/subscription\"\nimport { absoluteUrl } from \"@/lib/utils\"\n\nconst billingUrl = absoluteUrl(\"/dashboard/billing\")\n\nexport async function GET(req: Request) {\n  try {\n    const session = await getServerSession(authOptions)\n\n    if (!session?.user || !session?.user.email) {\n      return new Response(null, { status: 403 })\n    }\n\n    const subscriptionPlan = await getUserSubscriptionPlan(session.user.id)\n\n    // The user is on the pro plan.\n    // Create a portal session to manage subscription.\n    if (subscriptionPlan.isPro && subscriptionPlan.stripeCustomerId) {\n      const stripeSession = await stripe.billingPortal.sessions.create({\n        customer: subscriptionPlan.stripeCustomerId,\n        return_url: billingUrl,\n      })\n\n      return new Response(JSON.stringify({ url: stripeSession.url }))\n    }\n\n    // The user is on the free plan.\n    // Create a checkout session to upgrade.\n    const stripeSession = await stripe.checkout.sessions.create({\n      success_url: billingUrl,\n      cancel_url: billingUrl,\n      payment_method_types: [\"card\"],\n      mode: \"subscription\",\n      billing_address_collection: \"auto\",\n      customer_email: session.user.email,\n      line_items: [\n        {\n          price: proPlan.stripePriceId,\n          quantity: 1,\n        },\n      ],\n      metadata: {\n        userId: session.user.id,\n      },\n    })\n\n    return new Response(JSON.stringify({ url: stripeSession.url }))\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      return new Response(JSON.stringify(error.issues), { status: 422 })\n    }\n\n    return new Response(null, { status: 500 })\n  }\n}\n"
  },
  {
    "path": "app/api/webhooks/stripe/route.ts",
    "content": "import { headers } from \"next/headers\"\nimport Stripe from \"stripe\"\n\nimport { env } from \"@/env.mjs\"\nimport { db } from \"@/lib/db\"\nimport { stripe } from \"@/lib/stripe\"\n\nexport async function POST(req: Request) {\n  const body = await req.text()\n  const signature = headers().get(\"Stripe-Signature\") as string\n\n  let event: Stripe.Event\n\n  try {\n    event = stripe.webhooks.constructEvent(\n      body,\n      signature,\n      env.STRIPE_WEBHOOK_SECRET\n    )\n  } catch (error) {\n    return new Response(`Webhook Error: ${error.message}`, { status: 400 })\n  }\n\n  const session = event.data.object as Stripe.Checkout.Session\n\n  if (event.type === \"checkout.session.completed\") {\n    // Retrieve the subscription details from Stripe.\n    const subscription = await stripe.subscriptions.retrieve(\n      session.subscription as string\n    )\n\n    // Update the user stripe into in our database.\n    // Since this is the initial subscription, we need to update\n    // the subscription id and customer id.\n    await db.user.update({\n      where: {\n        id: session?.metadata?.userId,\n      },\n      data: {\n        stripeSubscriptionId: subscription.id,\n        stripeCustomerId: subscription.customer as string,\n        stripePriceId: subscription.items.data[0].price.id,\n        stripeCurrentPeriodEnd: new Date(\n          subscription.current_period_end * 1000\n        ),\n      },\n    })\n  }\n\n  if (event.type === \"invoice.payment_succeeded\") {\n    // Retrieve the subscription details from Stripe.\n    const subscription = await stripe.subscriptions.retrieve(\n      session.subscription as string\n    )\n\n    // Update the price id and set the new period end.\n    await db.user.update({\n      where: {\n        stripeSubscriptionId: subscription.id,\n      },\n      data: {\n        stripePriceId: subscription.items.data[0].price.id,\n        stripeCurrentPeriodEnd: new Date(\n          subscription.current_period_end * 1000\n        ),\n      },\n    })\n  }\n\n  return new Response(null, { status: 200 })\n}\n"
  },
  {
    "path": "app/layout.tsx",
    "content": "import { Inter as FontSans } from \"next/font/google\"\nimport localFont from \"next/font/local\"\n\nimport \"@/styles/globals.css\"\nimport { siteConfig } from \"@/config/site\"\nimport { absoluteUrl, cn } from \"@/lib/utils\"\nimport { Toaster } from \"@/components/ui/toaster\"\nimport { Analytics } from \"@/components/analytics\"\nimport { TailwindIndicator } from \"@/components/tailwind-indicator\"\nimport { ThemeProvider } from \"@/components/theme-provider\"\n\nconst fontSans = FontSans({\n  subsets: [\"latin\"],\n  variable: \"--font-sans\",\n})\n\n// Font files can be colocated inside of `pages`\nconst fontHeading = localFont({\n  src: \"../assets/fonts/CalSans-SemiBold.woff2\",\n  variable: \"--font-heading\",\n})\n\ninterface RootLayoutProps {\n  children: React.ReactNode\n}\n\nexport const metadata = {\n  title: {\n    default: siteConfig.name,\n    template: `%s | ${siteConfig.name}`,\n  },\n  description: siteConfig.description,\n  keywords: [\n    \"Next.js\",\n    \"React\",\n    \"Tailwind CSS\",\n    \"Server Components\",\n    \"Radix UI\",\n  ],\n  authors: [\n    {\n      name: \"shadcn\",\n      url: \"https://shadcn.com\",\n    },\n  ],\n  creator: \"shadcn\",\n  themeColor: [\n    { media: \"(prefers-color-scheme: light)\", color: \"white\" },\n    { media: \"(prefers-color-scheme: dark)\", color: \"black\" },\n  ],\n  openGraph: {\n    type: \"website\",\n    locale: \"en_US\",\n    url: siteConfig.url,\n    title: siteConfig.name,\n    description: siteConfig.description,\n    siteName: siteConfig.name,\n  },\n  twitter: {\n    card: \"summary_large_image\",\n    title: siteConfig.name,\n    description: siteConfig.description,\n    images: [`${siteConfig.url}/og.jpg`],\n    creator: \"@shadcn\",\n  },\n  icons: {\n    icon: \"/favicon.ico\",\n    shortcut: \"/favicon-16x16.png\",\n    apple: \"/apple-touch-icon.png\",\n  },\n  manifest: `${siteConfig.url}/site.webmanifest`,\n}\n\nexport default function RootLayout({ children }: RootLayoutProps) {\n  return (\n    <html lang=\"en\" suppressHydrationWarning>\n      <head />\n      <body\n        className={cn(\n          \"min-h-screen bg-background font-sans antialiased\",\n          fontSans.variable,\n          fontHeading.variable\n        )}\n      >\n        <ThemeProvider attribute=\"class\" defaultTheme=\"system\" enableSystem>\n          {children}\n          <Analytics />\n          <Toaster />\n          <TailwindIndicator />\n        </ThemeProvider>\n      </body>\n    </html>\n  )\n}\n"
  },
  {
    "path": "app/robots.ts",
    "content": "import { MetadataRoute } from \"next\"\n\nexport default function robots(): MetadataRoute.Robots {\n  return {\n    rules: {\n      userAgent: \"*\",\n      allow: \"/\",\n    },\n  }\n}\n"
  },
  {
    "path": "components/analytics.tsx",
    "content": "\"use client\"\n\nimport { Analytics as VercelAnalytics } from \"@vercel/analytics/react\"\n\nexport function Analytics() {\n  return <VercelAnalytics />\n}\n"
  },
  {
    "path": "components/billing-form.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { UserSubscriptionPlan } from \"types\"\nimport { cn, formatDate } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { toast } from \"@/components/ui/use-toast\"\nimport { Icons } from \"@/components/icons\"\n\ninterface BillingFormProps extends React.HTMLAttributes<HTMLFormElement> {\n  subscriptionPlan: UserSubscriptionPlan & {\n    isCanceled: boolean\n  }\n}\n\nexport function BillingForm({\n  subscriptionPlan,\n  className,\n  ...props\n}: BillingFormProps) {\n  const [isLoading, setIsLoading] = React.useState<boolean>(false)\n\n  async function onSubmit(event) {\n    event.preventDefault()\n    setIsLoading(!isLoading)\n\n    // Get a Stripe session URL.\n    const response = await fetch(\"/api/users/stripe\")\n\n    if (!response?.ok) {\n      return toast({\n        title: \"Something went wrong.\",\n        description: \"Please refresh the page and try again.\",\n        variant: \"destructive\",\n      })\n    }\n\n    // Redirect to the Stripe session.\n    // This could be a checkout page for initial upgrade.\n    // Or portal to manage existing subscription.\n    const session = await response.json()\n    if (session) {\n      window.location.href = session.url\n    }\n  }\n\n  return (\n    <form className={cn(className)} onSubmit={onSubmit} {...props}>\n      <Card>\n        <CardHeader>\n          <CardTitle>Subscription Plan</CardTitle>\n          <CardDescription>\n            You are currently on the <strong>{subscriptionPlan.name}</strong>{\" \"}\n            plan.\n          </CardDescription>\n        </CardHeader>\n        <CardContent>{subscriptionPlan.description}</CardContent>\n        <CardFooter className=\"flex flex-col items-start space-y-2 md:flex-row md:justify-between md:space-x-0\">\n          <button\n            type=\"submit\"\n            className={cn(buttonVariants())}\n            disabled={isLoading}\n          >\n            {isLoading && (\n              <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n            )}\n            {subscriptionPlan.isPro ? \"Manage Subscription\" : \"Upgrade to PRO\"}\n          </button>\n          {subscriptionPlan.isPro ? (\n            <p className=\"rounded-full text-xs font-medium\">\n              {subscriptionPlan.isCanceled\n                ? \"Your plan will be canceled on \"\n                : \"Your plan renews on \"}\n              {formatDate(subscriptionPlan.stripeCurrentPeriodEnd)}.\n            </p>\n          ) : null}\n        </CardFooter>\n      </Card>\n    </form>\n  )\n}\n"
  },
  {
    "path": "components/callout.tsx",
    "content": "import { cn } from \"@/lib/utils\"\n\ninterface CalloutProps {\n  icon?: string\n  children?: React.ReactNode\n  type?: \"default\" | \"warning\" | \"danger\"\n}\n\nexport function Callout({\n  children,\n  icon,\n  type = \"default\",\n  ...props\n}: CalloutProps) {\n  return (\n    <div\n      className={cn(\"my-6 flex items-start rounded-md border border-l-4 p-4\", {\n        \"border-red-900 bg-red-50\": type === \"danger\",\n        \"border-yellow-900 bg-yellow-50\": type === \"warning\",\n      })}\n      {...props}\n    >\n      {icon && <span className=\"mr-4 text-2xl\">{icon}</span>}\n      <div>{children}</div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/card-skeleton.tsx",
    "content": "import { Card, CardContent, CardFooter, CardHeader } from \"@/components/ui/card\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\n\nexport function CardSkeleton() {\n  return (\n    <Card>\n      <CardHeader className=\"gap-2\">\n        <Skeleton className=\"h-5 w-1/5\" />\n        <Skeleton className=\"h-4 w-4/5\" />\n      </CardHeader>\n      <CardContent className=\"h-10\" />\n      <CardFooter>\n        <Skeleton className=\"h-8 w-[120px]\" />\n      </CardFooter>\n    </Card>\n  )\n}\n"
  },
  {
    "path": "components/editor.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport Link from \"next/link\"\nimport { useRouter } from \"next/navigation\"\nimport EditorJS from \"@editorjs/editorjs\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { Post } from \"@prisma/client\"\nimport { useForm } from \"react-hook-form\"\nimport TextareaAutosize from \"react-textarea-autosize\"\nimport * as z from \"zod\"\n\nimport \"@/styles/editor.css\"\nimport { cn } from \"@/lib/utils\"\nimport { postPatchSchema } from \"@/lib/validations/post\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { toast } from \"@/components/ui/use-toast\"\nimport { Icons } from \"@/components/icons\"\n\ninterface EditorProps {\n  post: Pick<Post, \"id\" | \"title\" | \"content\" | \"published\">\n}\n\ntype FormData = z.infer<typeof postPatchSchema>\n\nexport function Editor({ post }: EditorProps) {\n  const { register, handleSubmit } = useForm<FormData>({\n    resolver: zodResolver(postPatchSchema),\n  })\n  const ref = React.useRef<EditorJS>()\n  const router = useRouter()\n  const [isSaving, setIsSaving] = React.useState<boolean>(false)\n  const [isMounted, setIsMounted] = React.useState<boolean>(false)\n\n  const initializeEditor = React.useCallback(async () => {\n    const EditorJS = (await import(\"@editorjs/editorjs\")).default\n    const Header = (await import(\"@editorjs/header\")).default\n    const Embed = (await import(\"@editorjs/embed\")).default\n    const Table = (await import(\"@editorjs/table\")).default\n    const List = (await import(\"@editorjs/list\")).default\n    const Code = (await import(\"@editorjs/code\")).default\n    const LinkTool = (await import(\"@editorjs/link\")).default\n    const InlineCode = (await import(\"@editorjs/inline-code\")).default\n\n    const body = postPatchSchema.parse(post)\n\n    if (!ref.current) {\n      const editor = new EditorJS({\n        holder: \"editor\",\n        onReady() {\n          ref.current = editor\n        },\n        placeholder: \"Type here to write your post...\",\n        inlineToolbar: true,\n        data: body.content,\n        tools: {\n          header: Header,\n          linkTool: LinkTool,\n          list: List,\n          code: Code,\n          inlineCode: InlineCode,\n          table: Table,\n          embed: Embed,\n        },\n      })\n    }\n  }, [post])\n\n  React.useEffect(() => {\n    if (typeof window !== \"undefined\") {\n      setIsMounted(true)\n    }\n  }, [])\n\n  React.useEffect(() => {\n    if (isMounted) {\n      initializeEditor()\n\n      return () => {\n        ref.current?.destroy()\n        ref.current = undefined\n      }\n    }\n  }, [isMounted, initializeEditor])\n\n  async function onSubmit(data: FormData) {\n    setIsSaving(true)\n\n    const blocks = await ref.current?.save()\n\n    const response = await fetch(`/api/posts/${post.id}`, {\n      method: \"PATCH\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        title: data.title,\n        content: blocks,\n      }),\n    })\n\n    setIsSaving(false)\n\n    if (!response?.ok) {\n      return toast({\n        title: \"Something went wrong.\",\n        description: \"Your post was not saved. Please try again.\",\n        variant: \"destructive\",\n      })\n    }\n\n    router.refresh()\n\n    return toast({\n      description: \"Your post has been saved.\",\n    })\n  }\n\n  if (!isMounted) {\n    return null\n  }\n\n  return (\n    <form onSubmit={handleSubmit(onSubmit)}>\n      <div className=\"grid w-full gap-10\">\n        <div className=\"flex w-full items-center justify-between\">\n          <div className=\"flex items-center space-x-10\">\n            <Link\n              href=\"/dashboard\"\n              className={cn(buttonVariants({ variant: \"ghost\" }))}\n            >\n              <>\n                <Icons.chevronLeft className=\"mr-2 h-4 w-4\" />\n                Back\n              </>\n            </Link>\n            <p className=\"text-sm text-muted-foreground\">\n              {post.published ? \"Published\" : \"Draft\"}\n            </p>\n          </div>\n          <button type=\"submit\" className={cn(buttonVariants())}>\n            {isSaving && (\n              <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n            )}\n            <span>Save</span>\n          </button>\n        </div>\n        <div className=\"prose prose-stone mx-auto w-[800px] dark:prose-invert\">\n          <TextareaAutosize\n            autoFocus\n            id=\"title\"\n            defaultValue={post.title}\n            placeholder=\"Post title\"\n            className=\"w-full resize-none appearance-none overflow-hidden bg-transparent text-5xl font-bold focus:outline-none\"\n            {...register(\"title\")}\n          />\n          <div id=\"editor\" className=\"min-h-[500px]\" />\n          <p className=\"text-sm text-gray-500\">\n            Use{\" \"}\n            <kbd className=\"rounded-md border bg-muted px-1 text-xs uppercase\">\n              Tab\n            </kbd>{\" \"}\n            to open the command menu.\n          </p>\n        </div>\n      </div>\n    </form>\n  )\n}\n"
  },
  {
    "path": "components/empty-placeholder.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Icons } from \"@/components/icons\"\n\ninterface EmptyPlaceholderProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport function EmptyPlaceholder({\n  className,\n  children,\n  ...props\n}: EmptyPlaceholderProps) {\n  return (\n    <div\n      className={cn(\n        \"flex min-h-[400px] flex-col items-center justify-center rounded-md border border-dashed p-8 text-center animate-in fade-in-50\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"mx-auto flex max-w-[420px] flex-col items-center justify-center text-center\">\n        {children}\n      </div>\n    </div>\n  )\n}\n\ninterface EmptyPlaceholderIconProps\n  extends Partial<React.SVGProps<SVGSVGElement>> {\n  name: keyof typeof Icons\n}\n\nEmptyPlaceholder.Icon = function EmptyPlaceHolderIcon({\n  name,\n  className,\n  ...props\n}: EmptyPlaceholderIconProps) {\n  const Icon = Icons[name]\n\n  if (!Icon) {\n    return null\n  }\n\n  return (\n    <div className=\"flex h-20 w-20 items-center justify-center rounded-full bg-muted\">\n      <Icon className={cn(\"h-10 w-10\", className)} {...props} />\n    </div>\n  )\n}\n\ninterface EmptyPlacholderTitleProps\n  extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nEmptyPlaceholder.Title = function EmptyPlaceholderTitle({\n  className,\n  ...props\n}: EmptyPlacholderTitleProps) {\n  return (\n    <h2 className={cn(\"mt-6 text-xl font-semibold\", className)} {...props} />\n  )\n}\n\ninterface EmptyPlacholderDescriptionProps\n  extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nEmptyPlaceholder.Description = function EmptyPlaceholderDescription({\n  className,\n  ...props\n}: EmptyPlacholderDescriptionProps) {\n  return (\n    <p\n      className={cn(\n        \"mb-8 mt-2 text-center text-sm font-normal leading-6 text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n"
  },
  {
    "path": "components/header.tsx",
    "content": "interface DashboardHeaderProps {\n  heading: string\n  text?: string\n  children?: React.ReactNode\n}\n\nexport function DashboardHeader({\n  heading,\n  text,\n  children,\n}: DashboardHeaderProps) {\n  return (\n    <div className=\"flex items-center justify-between px-2\">\n      <div className=\"grid gap-1\">\n        <h1 className=\"font-heading text-3xl md:text-4xl\">{heading}</h1>\n        {text && <p className=\"text-lg text-muted-foreground\">{text}</p>}\n      </div>\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/icons.tsx",
    "content": "import {\n  AlertTriangle,\n  ArrowRight,\n  Check,\n  ChevronLeft,\n  ChevronRight,\n  Command,\n  CreditCard,\n  File,\n  FileText,\n  HelpCircle,\n  Image,\n  Laptop,\n  Loader2,\n  LucideProps,\n  Moon,\n  MoreVertical,\n  Pizza,\n  Plus,\n  Settings,\n  SunMedium,\n  Trash,\n  Twitter,\n  User,\n  X,\n  type Icon as LucideIcon,\n} from \"lucide-react\"\n\nexport type Icon = LucideIcon\n\nexport const Icons = {\n  logo: Command,\n  close: X,\n  spinner: Loader2,\n  chevronLeft: ChevronLeft,\n  chevronRight: ChevronRight,\n  trash: Trash,\n  post: FileText,\n  page: File,\n  media: Image,\n  settings: Settings,\n  billing: CreditCard,\n  ellipsis: MoreVertical,\n  add: Plus,\n  warning: AlertTriangle,\n  user: User,\n  arrowRight: ArrowRight,\n  help: HelpCircle,\n  pizza: Pizza,\n  sun: SunMedium,\n  moon: Moon,\n  laptop: Laptop,\n  gitHub: ({ ...props }: LucideProps) => (\n    <svg\n      aria-hidden=\"true\"\n      focusable=\"false\"\n      data-prefix=\"fab\"\n      data-icon=\"github\"\n      role=\"img\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 496 512\"\n      {...props}\n    >\n      <path\n        fill=\"currentColor\"\n        d=\"M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z\"\n      ></path>\n    </svg>\n  ),\n  twitter: Twitter,\n  check: Check,\n}\n"
  },
  {
    "path": "components/main-nav.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport Link from \"next/link\"\nimport { useSelectedLayoutSegment } from \"next/navigation\"\n\nimport { MainNavItem } from \"types\"\nimport { siteConfig } from \"@/config/site\"\nimport { cn } from \"@/lib/utils\"\nimport { Icons } from \"@/components/icons\"\nimport { MobileNav } from \"@/components/mobile-nav\"\n\ninterface MainNavProps {\n  items?: MainNavItem[]\n  children?: React.ReactNode\n}\n\nexport function MainNav({ items, children }: MainNavProps) {\n  const segment = useSelectedLayoutSegment()\n  const [showMobileMenu, setShowMobileMenu] = React.useState<boolean>(false)\n\n  return (\n    <div className=\"flex gap-6 md:gap-10\">\n      <Link href=\"/\" className=\"hidden items-center space-x-2 md:flex\">\n        <Icons.logo />\n        <span className=\"hidden font-bold sm:inline-block\">\n          {siteConfig.name}\n        </span>\n      </Link>\n      {items?.length ? (\n        <nav className=\"hidden gap-6 md:flex\">\n          {items?.map((item, index) => (\n            <Link\n              key={index}\n              href={item.disabled ? \"#\" : item.href}\n              className={cn(\n                \"flex items-center text-lg font-medium transition-colors hover:text-foreground/80 sm:text-sm\",\n                item.href.startsWith(`/${segment}`)\n                  ? \"text-foreground\"\n                  : \"text-foreground/60\",\n                item.disabled && \"cursor-not-allowed opacity-80\"\n              )}\n            >\n              {item.title}\n            </Link>\n          ))}\n        </nav>\n      ) : null}\n      <button\n        className=\"flex items-center space-x-2 md:hidden\"\n        onClick={() => setShowMobileMenu(!showMobileMenu)}\n      >\n        {showMobileMenu ? <Icons.close /> : <Icons.logo />}\n        <span className=\"font-bold\">Menu</span>\n      </button>\n      {showMobileMenu && items && (\n        <MobileNav items={items}>{children}</MobileNav>\n      )}\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/mdx-card.tsx",
    "content": "import Link from \"next/link\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n  href?: string\n  disabled?: boolean\n}\n\nexport function MdxCard({\n  href,\n  className,\n  children,\n  disabled,\n  ...props\n}: CardProps) {\n  return (\n    <div\n      className={cn(\n        \"group relative rounded-lg border p-6 shadow-md transition-shadow hover:shadow-lg\",\n        disabled && \"cursor-not-allowed opacity-60\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"flex flex-col justify-between space-y-4\">\n        <div className=\"space-y-2 [&>h3]:!mt-0 [&>h4]:!mt-0 [&>p]:text-muted-foreground\">\n          {children}\n        </div>\n      </div>\n      {href && (\n        <Link href={disabled ? \"#\" : href} className=\"absolute inset-0\">\n          <span className=\"sr-only\">View</span>\n        </Link>\n      )}\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/mdx-components.tsx",
    "content": "import * as React from \"react\"\nimport Image from \"next/image\"\nimport { useMDXComponent } from \"next-contentlayer/hooks\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Callout } from \"@/components/callout\"\nimport { MdxCard } from \"@/components/mdx-card\"\n\nconst components = {\n  h1: ({ className, ...props }) => (\n    <h1\n      className={cn(\n        \"mt-2 scroll-m-20 text-4xl font-bold tracking-tight\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  h2: ({ className, ...props }) => (\n    <h2\n      className={cn(\n        \"mt-10 scroll-m-20 border-b pb-1 text-3xl font-semibold tracking-tight first:mt-0\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  h3: ({ className, ...props }) => (\n    <h3\n      className={cn(\n        \"mt-8 scroll-m-20 text-2xl font-semibold tracking-tight\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  h4: ({ className, ...props }) => (\n    <h4\n      className={cn(\n        \"mt-8 scroll-m-20 text-xl font-semibold tracking-tight\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  h5: ({ className, ...props }) => (\n    <h5\n      className={cn(\n        \"mt-8 scroll-m-20 text-lg font-semibold tracking-tight\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  h6: ({ className, ...props }) => (\n    <h6\n      className={cn(\n        \"mt-8 scroll-m-20 text-base font-semibold tracking-tight\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  a: ({ className, ...props }) => (\n    <a\n      className={cn(\"font-medium underline underline-offset-4\", className)}\n      {...props}\n    />\n  ),\n  p: ({ className, ...props }) => (\n    <p\n      className={cn(\"leading-7 [&:not(:first-child)]:mt-6\", className)}\n      {...props}\n    />\n  ),\n  ul: ({ className, ...props }) => (\n    <ul className={cn(\"my-6 ml-6 list-disc\", className)} {...props} />\n  ),\n  ol: ({ className, ...props }) => (\n    <ol className={cn(\"my-6 ml-6 list-decimal\", className)} {...props} />\n  ),\n  li: ({ className, ...props }) => (\n    <li className={cn(\"mt-2\", className)} {...props} />\n  ),\n  blockquote: ({ className, ...props }) => (\n    <blockquote\n      className={cn(\n        \"mt-6 border-l-2 pl-6 italic [&>*]:text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  img: ({\n    className,\n    alt,\n    ...props\n  }: React.ImgHTMLAttributes<HTMLImageElement>) => (\n    // eslint-disable-next-line @next/next/no-img-element\n    <img className={cn(\"rounded-md border\", className)} alt={alt} {...props} />\n  ),\n  hr: ({ ...props }) => <hr className=\"my-4 md:my-8\" {...props} />,\n  table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (\n    <div className=\"my-6 w-full overflow-y-auto\">\n      <table className={cn(\"w-full\", className)} {...props} />\n    </div>\n  ),\n  tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (\n    <tr\n      className={cn(\"m-0 border-t p-0 even:bg-muted\", className)}\n      {...props}\n    />\n  ),\n  th: ({ className, ...props }) => (\n    <th\n      className={cn(\n        \"border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  td: ({ className, ...props }) => (\n    <td\n      className={cn(\n        \"border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  pre: ({ className, ...props }) => (\n    <pre\n      className={cn(\n        \"mb-4 mt-6 overflow-x-auto rounded-lg border bg-black py-4\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  code: ({ className, ...props }) => (\n    <code\n      className={cn(\n        \"relative rounded border px-[0.3rem] py-[0.2rem] font-mono text-sm\",\n        className\n      )}\n      {...props}\n    />\n  ),\n  Image,\n  Callout,\n  Card: MdxCard,\n}\n\ninterface MdxProps {\n  code: string\n}\n\nexport function Mdx({ code }: MdxProps) {\n  const Component = useMDXComponent(code)\n\n  return (\n    <div className=\"mdx\">\n      <Component components={components} />\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/mobile-nav.tsx",
    "content": "import * as React from \"react\"\nimport Link from \"next/link\"\n\nimport { MainNavItem } from \"types\"\nimport { siteConfig } from \"@/config/site\"\nimport { cn } from \"@/lib/utils\"\nimport { useLockBody } from \"@/hooks/use-lock-body\"\nimport { Icons } from \"@/components/icons\"\n\ninterface MobileNavProps {\n  items: MainNavItem[]\n  children?: React.ReactNode\n}\n\nexport function MobileNav({ items, children }: MobileNavProps) {\n  useLockBody()\n\n  return (\n    <div\n      className={cn(\n        \"fixed inset-0 top-16 z-50 grid h-[calc(100vh-4rem)] grid-flow-row auto-rows-max overflow-auto p-6 pb-32 shadow-md animate-in slide-in-from-bottom-80 md:hidden\"\n      )}\n    >\n      <div className=\"relative z-20 grid gap-6 rounded-md bg-popover p-4 text-popover-foreground shadow-md\">\n        <Link href=\"/\" className=\"flex items-center space-x-2\">\n          <Icons.logo />\n          <span className=\"font-bold\">{siteConfig.name}</span>\n        </Link>\n        <nav className=\"grid grid-flow-row auto-rows-max text-sm\">\n          {items.map((item, index) => (\n            <Link\n              key={index}\n              href={item.disabled ? \"#\" : item.href}\n              className={cn(\n                \"flex w-full items-center rounded-md p-2 text-sm font-medium hover:underline\",\n                item.disabled && \"cursor-not-allowed opacity-60\"\n              )}\n            >\n              {item.title}\n            </Link>\n          ))}\n        </nav>\n        {children}\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/mode-toggle.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { useTheme } from \"next-themes\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { Icons } from \"@/components/icons\"\n\nexport function ModeToggle() {\n  const { setTheme } = useTheme()\n\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger asChild>\n        <Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 px-0\">\n          <Icons.sun className=\"rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0\" />\n          <Icons.moon className=\"absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100\" />\n          <span className=\"sr-only\">Toggle theme</span>\n        </Button>\n      </DropdownMenuTrigger>\n      <DropdownMenuContent align=\"end\">\n        <DropdownMenuItem onClick={() => setTheme(\"light\")}>\n          <Icons.sun className=\"mr-2 h-4 w-4\" />\n          <span>Light</span>\n        </DropdownMenuItem>\n        <DropdownMenuItem onClick={() => setTheme(\"dark\")}>\n          <Icons.moon className=\"mr-2 h-4 w-4\" />\n          <span>Dark</span>\n        </DropdownMenuItem>\n        <DropdownMenuItem onClick={() => setTheme(\"system\")}>\n          <Icons.laptop className=\"mr-2 h-4 w-4\" />\n          <span>System</span>\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n"
  },
  {
    "path": "components/nav.tsx",
    "content": "\"use client\"\n\nimport Link from \"next/link\"\nimport { usePathname } from \"next/navigation\"\n\nimport { SidebarNavItem } from \"types\"\nimport { cn } from \"@/lib/utils\"\nimport { Icons } from \"@/components/icons\"\n\ninterface DashboardNavProps {\n  items: SidebarNavItem[]\n}\n\nexport function DashboardNav({ items }: DashboardNavProps) {\n  const path = usePathname()\n\n  if (!items?.length) {\n    return null\n  }\n\n  return (\n    <nav className=\"grid items-start gap-2\">\n      {items.map((item, index) => {\n        const Icon = Icons[item.icon || \"arrowRight\"]\n        return (\n          item.href && (\n            <Link key={index} href={item.disabled ? \"/\" : item.href}>\n              <span\n                className={cn(\n                  \"group flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground\",\n                  path === item.href ? \"bg-accent\" : \"transparent\",\n                  item.disabled && \"cursor-not-allowed opacity-80\"\n                )}\n              >\n                <Icon className=\"mr-2 h-4 w-4\" />\n                <span>{item.title}</span>\n              </span>\n            </Link>\n          )\n        )\n      })}\n    </nav>\n  )\n}\n"
  },
  {
    "path": "components/page-header.tsx",
    "content": "import { cn } from \"@/lib/utils\"\n\ninterface DocsPageHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n  heading: string\n  text?: string\n}\n\nexport function DocsPageHeader({\n  heading,\n  text,\n  className,\n  ...props\n}: DocsPageHeaderProps) {\n  return (\n    <>\n      <div className={cn(\"space-y-4\", className)} {...props}>\n        <h1 className=\"inline-block font-heading text-4xl lg:text-5xl\">\n          {heading}\n        </h1>\n        {text && <p className=\"text-xl text-muted-foreground\">{text}</p>}\n      </div>\n      <hr className=\"my-4\" />\n    </>\n  )\n}\n"
  },
  {
    "path": "components/pager.tsx",
    "content": "import Link from \"next/link\"\nimport { Doc } from \"contentlayer/generated\"\n\nimport { docsConfig } from \"@/config/docs\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Icons } from \"@/components/icons\"\n\ninterface DocsPagerProps {\n  doc: Doc\n}\n\nexport function DocsPager({ doc }: DocsPagerProps) {\n  const pager = getPagerForDoc(doc)\n\n  if (!pager) {\n    return null\n  }\n\n  return (\n    <div className=\"flex flex-row items-center justify-between\">\n      {pager?.prev && (\n        <Link\n          href={pager.prev.href}\n          className={cn(buttonVariants({ variant: \"ghost\" }))}\n        >\n          <Icons.chevronLeft className=\"mr-2 h-4 w-4\" />\n          {pager.prev.title}\n        </Link>\n      )}\n      {pager?.next && (\n        <Link\n          href={pager.next.href}\n          className={cn(buttonVariants({ variant: \"ghost\" }), \"ml-auto\")}\n        >\n          {pager.next.title}\n          <Icons.chevronRight className=\"ml-2 h-4 w-4\" />\n        </Link>\n      )}\n    </div>\n  )\n}\n\nexport function getPagerForDoc(doc: Doc) {\n  const flattenedLinks = [null, ...flatten(docsConfig.sidebarNav), null]\n  const activeIndex = flattenedLinks.findIndex(\n    (link) => doc.slug === link?.href\n  )\n  const prev = activeIndex !== 0 ? flattenedLinks[activeIndex - 1] : null\n  const next =\n    activeIndex !== flattenedLinks.length - 1\n      ? flattenedLinks[activeIndex + 1]\n      : null\n  return {\n    prev,\n    next,\n  }\n}\n\nexport function flatten(links: { items? }[]) {\n  return links.reduce((flat, link) => {\n    return flat.concat(link.items ? flatten(link.items) : link)\n  }, [])\n}\n"
  },
  {
    "path": "components/post-create-button.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { useRouter } from \"next/navigation\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ButtonProps, buttonVariants } from \"@/components/ui/button\"\nimport { toast } from \"@/components/ui/use-toast\"\nimport { Icons } from \"@/components/icons\"\n\ninterface PostCreateButtonProps extends ButtonProps {}\n\nexport function PostCreateButton({\n  className,\n  variant,\n  ...props\n}: PostCreateButtonProps) {\n  const router = useRouter()\n  const [isLoading, setIsLoading] = React.useState<boolean>(false)\n\n  async function onClick() {\n    setIsLoading(true)\n\n    const response = await fetch(\"/api/posts\", {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        title: \"Untitled Post\",\n      }),\n    })\n\n    setIsLoading(false)\n\n    if (!response?.ok) {\n      if (response.status === 402) {\n        return toast({\n          title: \"Limit of 3 posts reached.\",\n          description: \"Please upgrade to the PRO plan.\",\n          variant: \"destructive\",\n        })\n      }\n\n      return toast({\n        title: \"Something went wrong.\",\n        description: \"Your post was not created. Please try again.\",\n        variant: \"destructive\",\n      })\n    }\n\n    const post = await response.json()\n\n    // This forces a cache invalidation.\n    router.refresh()\n\n    router.push(`/editor/${post.id}`)\n  }\n\n  return (\n    <button\n      onClick={onClick}\n      className={cn(\n        buttonVariants({ variant }),\n        {\n          \"cursor-not-allowed opacity-60\": isLoading,\n        },\n        className\n      )}\n      disabled={isLoading}\n      {...props}\n    >\n      {isLoading ? (\n        <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n      ) : (\n        <Icons.add className=\"mr-2 h-4 w-4\" />\n      )}\n      New post\n    </button>\n  )\n}\n"
  },
  {
    "path": "components/post-item.tsx",
    "content": "import Link from \"next/link\"\nimport { Post } from \"@prisma/client\"\n\nimport { formatDate } from \"@/lib/utils\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { PostOperations } from \"@/components/post-operations\"\n\ninterface PostItemProps {\n  post: Pick<Post, \"id\" | \"title\" | \"published\" | \"createdAt\">\n}\n\nexport function PostItem({ post }: PostItemProps) {\n  return (\n    <div className=\"flex items-center justify-between p-4\">\n      <div className=\"grid gap-1\">\n        <Link\n          href={`/editor/${post.id}`}\n          className=\"font-semibold hover:underline\"\n        >\n          {post.title}\n        </Link>\n        <div>\n          <p className=\"text-sm text-muted-foreground\">\n            {formatDate(post.createdAt?.toDateString())}\n          </p>\n        </div>\n      </div>\n      <PostOperations post={{ id: post.id, title: post.title }} />\n    </div>\n  )\n}\n\nPostItem.Skeleton = function PostItemSkeleton() {\n  return (\n    <div className=\"p-4\">\n      <div className=\"space-y-3\">\n        <Skeleton className=\"h-5 w-2/5\" />\n        <Skeleton className=\"h-4 w-4/5\" />\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/post-operations.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport Link from \"next/link\"\nimport { useRouter } from \"next/navigation\"\nimport { Post } from \"@prisma/client\"\n\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { toast } from \"@/components/ui/use-toast\"\nimport { Icons } from \"@/components/icons\"\n\nasync function deletePost(postId: string) {\n  const response = await fetch(`/api/posts/${postId}`, {\n    method: \"DELETE\",\n  })\n\n  if (!response?.ok) {\n    toast({\n      title: \"Something went wrong.\",\n      description: \"Your post was not deleted. Please try again.\",\n      variant: \"destructive\",\n    })\n  }\n\n  return true\n}\n\ninterface PostOperationsProps {\n  post: Pick<Post, \"id\" | \"title\">\n}\n\nexport function PostOperations({ post }: PostOperationsProps) {\n  const router = useRouter()\n  const [showDeleteAlert, setShowDeleteAlert] = React.useState<boolean>(false)\n  const [isDeleteLoading, setIsDeleteLoading] = React.useState<boolean>(false)\n\n  return (\n    <>\n      <DropdownMenu>\n        <DropdownMenuTrigger className=\"flex h-8 w-8 items-center justify-center rounded-md border transition-colors hover:bg-muted\">\n          <Icons.ellipsis className=\"h-4 w-4\" />\n          <span className=\"sr-only\">Open</span>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent align=\"end\">\n          <DropdownMenuItem>\n            <Link href={`/editor/${post.id}`} className=\"flex w-full\">\n              Edit\n            </Link>\n          </DropdownMenuItem>\n          <DropdownMenuSeparator />\n          <DropdownMenuItem\n            className=\"flex cursor-pointer items-center text-destructive focus:text-destructive\"\n            onSelect={() => setShowDeleteAlert(true)}\n          >\n            Delete\n          </DropdownMenuItem>\n        </DropdownMenuContent>\n      </DropdownMenu>\n      <AlertDialog open={showDeleteAlert} onOpenChange={setShowDeleteAlert}>\n        <AlertDialogContent>\n          <AlertDialogHeader>\n            <AlertDialogTitle>\n              Are you sure you want to delete this post?\n            </AlertDialogTitle>\n            <AlertDialogDescription>\n              This action cannot be undone.\n            </AlertDialogDescription>\n          </AlertDialogHeader>\n          <AlertDialogFooter>\n            <AlertDialogCancel>Cancel</AlertDialogCancel>\n            <AlertDialogAction\n              onClick={async (event) => {\n                event.preventDefault()\n                setIsDeleteLoading(true)\n\n                const deleted = await deletePost(post.id)\n\n                if (deleted) {\n                  setIsDeleteLoading(false)\n                  setShowDeleteAlert(false)\n                  router.refresh()\n                }\n              }}\n              className=\"bg-red-600 focus:ring-red-600\"\n            >\n              {isDeleteLoading ? (\n                <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n              ) : (\n                <Icons.trash className=\"mr-2 h-4 w-4\" />\n              )}\n              <span>Delete</span>\n            </AlertDialogAction>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n    </>\n  )\n}\n"
  },
  {
    "path": "components/search.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Input } from \"@/components/ui/input\"\nimport { toast } from \"@/components/ui/use-toast\"\n\ninterface DocsSearchProps extends React.HTMLAttributes<HTMLFormElement> {}\n\nexport function DocsSearch({ className, ...props }: DocsSearchProps) {\n  function onSubmit(event: React.SyntheticEvent) {\n    event.preventDefault()\n\n    return toast({\n      title: \"Not implemented\",\n      description: \"We're still working on the search.\",\n    })\n  }\n\n  return (\n    <form\n      onSubmit={onSubmit}\n      className={cn(\"relative w-full\", className)}\n      {...props}\n    >\n      <Input\n        type=\"search\"\n        placeholder=\"Search documentation...\"\n        className=\"h-8 w-full sm:w-64 sm:pr-12\"\n      />\n      <kbd className=\"pointer-events-none absolute right-1.5 top-1.5 hidden h-5 select-none items-center gap-1 rounded border bg-background px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100 sm:flex\">\n        <span className=\"text-xs\">⌘</span>K\n      </kbd>\n    </form>\n  )\n}\n"
  },
  {
    "path": "components/shell.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface DashboardShellProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport function DashboardShell({\n  children,\n  className,\n  ...props\n}: DashboardShellProps) {\n  return (\n    <div className={cn(\"grid items-start gap-8\", className)} {...props}>\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/sidebar-nav.tsx",
    "content": "\"use client\"\n\nimport Link from \"next/link\"\nimport { usePathname } from \"next/navigation\"\n\nimport { SidebarNavItem } from \"types\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface DocsSidebarNavProps {\n  items: SidebarNavItem[]\n}\n\nexport function DocsSidebarNav({ items }: DocsSidebarNavProps) {\n  const pathname = usePathname()\n\n  return items.length ? (\n    <div className=\"w-full\">\n      {items.map((item, index) => (\n        <div key={index} className={cn(\"pb-8\")}>\n          <h4 className=\"mb-1 rounded-md px-2 py-1 text-sm font-medium\">\n            {item.title}\n          </h4>\n          {item.items ? (\n            <DocsSidebarNavItems items={item.items} pathname={pathname} />\n          ) : null}\n        </div>\n      ))}\n    </div>\n  ) : null\n}\n\ninterface DocsSidebarNavItemsProps {\n  items: SidebarNavItem[]\n  pathname: string | null\n}\n\nexport function DocsSidebarNavItems({\n  items,\n  pathname,\n}: DocsSidebarNavItemsProps) {\n  return items?.length ? (\n    <div className=\"grid grid-flow-row auto-rows-max text-sm\">\n      {items.map((item, index) =>\n        !item.disabled && item.href ? (\n          <Link\n            key={index}\n            href={item.href}\n            className={cn(\n              \"flex w-full items-center rounded-md p-2 hover:underline\",\n              {\n                \"bg-muted\": pathname === item.href,\n              }\n            )}\n            target={item.external ? \"_blank\" : \"\"}\n            rel={item.external ? \"noreferrer\" : \"\"}\n          >\n            {item.title}\n          </Link>\n        ) : (\n          <span className=\"flex w-full cursor-not-allowed items-center rounded-md p-2 opacity-60\">\n            {item.title}\n          </span>\n        )\n      )}\n    </div>\n  ) : null\n}\n"
  },
  {
    "path": "components/site-footer.tsx",
    "content": "import * as React from \"react\"\n\nimport { siteConfig } from \"@/config/site\"\nimport { cn } from \"@/lib/utils\"\nimport { Icons } from \"@/components/icons\"\nimport { ModeToggle } from \"@/components/mode-toggle\"\n\nexport function SiteFooter({ className }: React.HTMLAttributes<HTMLElement>) {\n  return (\n    <footer className={cn(className)}>\n      <div className=\"container flex flex-col items-center justify-between gap-4 py-10 md:h-24 md:flex-row md:py-0\">\n        <div className=\"flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0\">\n          <Icons.logo />\n          <p className=\"text-center text-sm leading-loose md:text-left\">\n            Built by{\" \"}\n            <a\n              href={siteConfig.links.twitter}\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"font-medium underline underline-offset-4\"\n            >\n              shadcn\n            </a>\n            . Hosted on{\" \"}\n            <a\n              href=\"https://vercel.com\"\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"font-medium underline underline-offset-4\"\n            >\n              Vercel\n            </a>\n            . Illustrations by{\" \"}\n            <a\n              href=\"https://popsy.co\"\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"font-medium underline underline-offset-4\"\n            >\n              Popsy\n            </a>\n            . The source code is available on{\" \"}\n            <a\n              href={siteConfig.links.github}\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"font-medium underline underline-offset-4\"\n            >\n              GitHub\n            </a>\n            .\n          </p>\n        </div>\n        <ModeToggle />\n      </div>\n    </footer>\n  )\n}\n"
  },
  {
    "path": "components/tailwind-indicator.tsx",
    "content": "export function TailwindIndicator() {\n  if (process.env.NODE_ENV === \"production\") return null\n\n  return (\n    <div className=\"fixed bottom-1 left-1 z-50 flex h-6 w-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-white\">\n      <div className=\"block sm:hidden\">xs</div>\n      <div className=\"hidden sm:block md:hidden lg:hidden xl:hidden 2xl:hidden\">\n        sm\n      </div>\n      <div className=\"hidden md:block lg:hidden xl:hidden 2xl:hidden\">md</div>\n      <div className=\"hidden lg:block xl:hidden 2xl:hidden\">lg</div>\n      <div className=\"hidden xl:block 2xl:hidden\">xl</div>\n      <div className=\"hidden 2xl:block\">2xl</div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/theme-provider.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ThemeProvider as NextThemesProvider } from \"next-themes\"\nimport { ThemeProviderProps } from \"next-themes/dist/types\"\n\nexport function ThemeProvider({ children, ...props }: ThemeProviderProps) {\n  return <NextThemesProvider {...props}>{children}</NextThemesProvider>\n}\n"
  },
  {
    "path": "components/toc.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { TableOfContents } from \"@/lib/toc\"\nimport { cn } from \"@/lib/utils\"\nimport { useMounted } from \"@/hooks/use-mounted\"\n\ninterface TocProps {\n  toc: TableOfContents\n}\n\nexport function DashboardTableOfContents({ toc }: TocProps) {\n  const itemIds = React.useMemo(\n    () =>\n      toc.items\n        ? toc.items\n            .flatMap((item) => [item.url, item?.items?.map((item) => item.url)])\n            .flat()\n            .filter(Boolean)\n            .map((id) => id?.split(\"#\")[1])\n        : [],\n    [toc]\n  )\n  const activeHeading = useActiveItem(itemIds)\n  const mounted = useMounted()\n\n  if (!toc?.items) {\n    return null\n  }\n\n  return mounted ? (\n    <div className=\"space-y-2\">\n      <p className=\"font-medium\">On This Page</p>\n      <Tree tree={toc} activeItem={activeHeading} />\n    </div>\n  ) : null\n}\n\nfunction useActiveItem(itemIds: (string | undefined)[]) {\n  const [activeId, setActiveId] = React.useState<string>(\"\")\n\n  React.useEffect(() => {\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          if (entry.isIntersecting) {\n            setActiveId(entry.target.id)\n          }\n        })\n      },\n      { rootMargin: `0% 0% -80% 0%` }\n    )\n\n    itemIds?.forEach((id) => {\n      if (!id) {\n        return\n      }\n\n      const element = document.getElementById(id)\n      if (element) {\n        observer.observe(element)\n      }\n    })\n\n    return () => {\n      itemIds?.forEach((id) => {\n        if (!id) {\n          return\n        }\n\n        const element = document.getElementById(id)\n        if (element) {\n          observer.unobserve(element)\n        }\n      })\n    }\n  }, [itemIds])\n\n  return activeId\n}\n\ninterface TreeProps {\n  tree: TableOfContents\n  level?: number\n  activeItem?: string | null\n}\n\nfunction Tree({ tree, level = 1, activeItem }: TreeProps) {\n  return tree?.items?.length && level < 3 ? (\n    <ul className={cn(\"m-0 list-none\", { \"pl-4\": level !== 1 })}>\n      {tree.items.map((item, index) => {\n        return (\n          <li key={index} className={cn(\"mt-0 pt-2\")}>\n            <a\n              href={item.url}\n              className={cn(\n                \"inline-block no-underline\",\n                item.url === `#${activeItem}`\n                  ? \"font-medium text-primary\"\n                  : \"text-sm text-muted-foreground\"\n              )}\n            >\n              {item.title}\n            </a>\n            {item.items?.length ? (\n              <Tree tree={item} level={level + 1} activeItem={activeItem} />\n            ) : null}\n          </li>\n        )\n      })}\n    </ul>\n  ) : null\n}\n"
  },
  {
    "path": "components/ui/accordion.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\"\nimport { ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Accordion = AccordionPrimitive.Root\n\nconst AccordionItem = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>\n>(({ className, ...props }, ref) => (\n  <AccordionPrimitive.Item\n    ref={ref}\n    className={cn(\"border-b\", className)}\n    {...props}\n  />\n))\nAccordionItem.displayName = \"AccordionItem\"\n\nconst AccordionTrigger = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n  <AccordionPrimitive.Header className=\"flex\">\n    <AccordionPrimitive.Trigger\n      ref={ref}\n      className={cn(\n        \"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <ChevronDown className=\"h-4 w-4 transition-transform duration-200\" />\n    </AccordionPrimitive.Trigger>\n  </AccordionPrimitive.Header>\n))\nAccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName\n\nconst AccordionContent = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n  <AccordionPrimitive.Content\n    ref={ref}\n    className={cn(\n      \"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\",\n      className\n    )}\n    {...props}\n  >\n    <div className=\"pb-4 pt-0\">{children}</div>\n  </AccordionPrimitive.Content>\n))\nAccordionContent.displayName = AccordionPrimitive.Content.displayName\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }\n"
  },
  {
    "path": "components/ui/alert-dialog.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\n\nconst AlertDialog = AlertDialogPrimitive.Root\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger\n\nconst AlertDialogPortal = ({\n  className,\n  children,\n  ...props\n}: AlertDialogPrimitive.AlertDialogPortalProps) => (\n  <AlertDialogPrimitive.Portal className={cn(className)} {...props}>\n    <div className=\"fixed inset-0 z-50 flex items-end justify-center sm:items-center\">\n      {children}\n    </div>\n  </AlertDialogPrimitive.Portal>\n)\nAlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName\n\nconst AlertDialogOverlay = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, children, ...props }, ref) => (\n  <AlertDialogPrimitive.Overlay\n    className={cn(\n      \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-opacity animate-in fade-in\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  />\n))\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName\n\nconst AlertDialogContent = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPortal>\n    <AlertDialogOverlay />\n    <AlertDialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-background p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full\",\n        className\n      )}\n      {...props}\n    />\n  </AlertDialogPortal>\n))\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName\n\nconst AlertDialogHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-2 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nAlertDialogHeader.displayName = \"AlertDialogHeader\"\n\nconst AlertDialogFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nAlertDialogFooter.displayName = \"AlertDialogFooter\"\n\nconst AlertDialogTitle = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Title\n    ref={ref}\n    className={cn(\"text-lg font-semibold\", className)}\n    {...props}\n  />\n))\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName\n\nconst AlertDialogDescription = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nAlertDialogDescription.displayName =\n  AlertDialogPrimitive.Description.displayName\n\nconst AlertDialogAction = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Action>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Action\n    ref={ref}\n    className={cn(buttonVariants(), className)}\n    {...props}\n  />\n))\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName\n\nconst AlertDialogCancel = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Cancel\n    ref={ref}\n    className={cn(\n      buttonVariants({ variant: \"outline\" }),\n      \"mt-2 sm:mt-0\",\n      className\n    )}\n    {...props}\n  />\n))\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName\n\nexport {\n  AlertDialog,\n  AlertDialogTrigger,\n  AlertDialogContent,\n  AlertDialogHeader,\n  AlertDialogFooter,\n  AlertDialogTitle,\n  AlertDialogDescription,\n  AlertDialogAction,\n  AlertDialogCancel,\n}\n"
  },
  {
    "path": "components/ui/alert.tsx",
    "content": "import * as React from \"react\"\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst alertVariants = cva(\n  \"relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-background text-foreground\",\n        destructive:\n          \"text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst Alert = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n>(({ className, variant, ...props }, ref) => (\n  <div\n    ref={ref}\n    role=\"alert\"\n    className={cn(alertVariants({ variant }), className)}\n    {...props}\n  />\n))\nAlert.displayName = \"Alert\"\n\nconst AlertTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h5\n    ref={ref}\n    className={cn(\"mb-1 font-medium leading-none tracking-tight\", className)}\n    {...props}\n  />\n))\nAlertTitle.displayName = \"AlertTitle\"\n\nconst AlertDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"text-sm [&_p]:leading-relaxed\", className)}\n    {...props}\n  />\n))\nAlertDescription.displayName = \"AlertDescription\"\n\nexport { Alert, AlertTitle, AlertDescription }\n"
  },
  {
    "path": "components/ui/aspect-ratio.tsx",
    "content": "\"use client\"\n\nimport * as AspectRatioPrimitive from \"@radix-ui/react-aspect-ratio\"\n\nconst AspectRatio = AspectRatioPrimitive.Root\n\nexport { AspectRatio }\n"
  },
  {
    "path": "components/ui/avatar.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Avatar = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full\",\n      className\n    )}\n    {...props}\n  />\n))\nAvatar.displayName = AvatarPrimitive.Root.displayName\n\nconst AvatarImage = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Image>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Image\n    ref={ref}\n    className={cn(\"aspect-square h-full w-full\", className)}\n    {...props}\n  />\n))\nAvatarImage.displayName = AvatarPrimitive.Image.displayName\n\nconst AvatarFallback = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Fallback>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Fallback\n    ref={ref}\n    className={cn(\n      \"flex h-full w-full items-center justify-center rounded-full bg-muted\",\n      className\n    )}\n    {...props}\n  />\n))\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName\n\nexport { Avatar, AvatarImage, AvatarFallback }\n"
  },
  {
    "path": "components/ui/badge.tsx",
    "content": "import * as React from \"react\"\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n  \"inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"bg-primary hover:bg-primary/80 border-transparent text-primary-foreground\",\n        secondary:\n          \"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground\",\n        destructive:\n          \"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground\",\n        outline: \"text-foreground\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nexport interface BadgeProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n  return (\n    <div className={cn(badgeVariants({ variant }), className)} {...props} />\n  )\n}\n\nexport { Badge, badgeVariants }\n"
  },
  {
    "path": "components/ui/button.tsx",
    "content": "import * as React from \"react\"\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"underline-offset-4 hover:underline text-primary\",\n      },\n      size: {\n        default: \"h-10 py-2 px-4\",\n        sm: \"h-9 px-3 rounded-md\",\n        lg: \"h-11 px-8 rounded-md\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, ...props }, ref) => {\n    return (\n      <button\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
  },
  {
    "path": "components/ui/calendar.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { DayPicker } from \"react-day-picker\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker>\n\nfunction Calendar({\n  className,\n  classNames,\n  showOutsideDays = true,\n  ...props\n}: CalendarProps) {\n  return (\n    <DayPicker\n      showOutsideDays={showOutsideDays}\n      className={cn(\"p-3\", className)}\n      classNames={{\n        months: \"flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0\",\n        month: \"space-y-4\",\n        caption: \"flex justify-center pt-1 relative items-center\",\n        caption_label: \"text-sm font-medium\",\n        nav: \"space-x-1 flex items-center\",\n        nav_button: cn(\n          buttonVariants({ variant: \"outline\" }),\n          \"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100\"\n        ),\n        nav_button_previous: \"absolute left-1\",\n        nav_button_next: \"absolute right-1\",\n        table: \"w-full border-collapse space-y-1\",\n        head_row: \"flex\",\n        head_cell:\n          \"text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]\",\n        row: \"flex w-full mt-2\",\n        cell: \"text-center text-sm p-0 relative [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20\",\n        day: cn(\n          buttonVariants({ variant: \"ghost\" }),\n          \"h-9 w-9 p-0 font-normal aria-selected:opacity-100\"\n        ),\n        day_selected:\n          \"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground\",\n        day_today: \"bg-accent text-accent-foreground\",\n        day_outside: \"text-muted-foreground opacity-50\",\n        day_disabled: \"text-muted-foreground opacity-50\",\n        day_range_middle:\n          \"aria-selected:bg-accent aria-selected:text-accent-foreground\",\n        day_hidden: \"invisible\",\n        ...classNames,\n      }}\n      components={{\n        IconLeft: ({ ...props }) => <ChevronLeft className=\"h-4 w-4\" />,\n        IconRight: ({ ...props }) => <ChevronRight className=\"h-4 w-4\" />,\n      }}\n      {...props}\n    />\n  )\n}\nCalendar.displayName = \"Calendar\"\n\nexport { Calendar }\n"
  },
  {
    "path": "components/ui/card.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Card = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\n      \"rounded-lg border bg-card text-card-foreground shadow-sm\",\n      className\n    )}\n    {...props}\n  />\n))\nCard.displayName = \"Card\"\n\nconst CardHeader = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n    {...props}\n  />\n))\nCardHeader.displayName = \"CardHeader\"\n\nconst CardTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h3\n    ref={ref}\n    className={cn(\n      \"text-lg font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n))\nCardTitle.displayName = \"CardTitle\"\n\nconst CardDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <p\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nCardDescription.displayName = \"CardDescription\"\n\nconst CardContent = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n))\nCardContent.displayName = \"CardContent\"\n\nconst CardFooter = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\" flex items-center p-6 pt-0\", className)}\n    {...props}\n  />\n))\nCardFooter.displayName = \"CardFooter\"\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }\n"
  },
  {
    "path": "components/ui/checkbox.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\"\nimport { Check } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Checkbox = React.forwardRef<\n  React.ElementRef<typeof CheckboxPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <CheckboxPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"peer h-4 w-4 shrink-0 rounded-sm border border-input ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary\",\n      className\n    )}\n    {...props}\n  >\n    <CheckboxPrimitive.Indicator\n      className={cn(\"flex items-center justify-center text-primary\")}\n    >\n      <Check className=\"h-4 w-4\" />\n    </CheckboxPrimitive.Indicator>\n  </CheckboxPrimitive.Root>\n))\nCheckbox.displayName = CheckboxPrimitive.Root.displayName\n\nexport { Checkbox }\n"
  },
  {
    "path": "components/ui/collapsible.tsx",
    "content": "\"use client\"\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\"\n\nconst Collapsible = CollapsiblePrimitive.Root\n\nconst CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger\n\nconst CollapsibleContent = CollapsiblePrimitive.CollapsibleContent\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent }\n"
  },
  {
    "path": "components/ui/command.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { DialogProps } from \"@radix-ui/react-dialog\"\nimport { Command as CommandPrimitive } from \"cmdk\"\nimport { Search } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Dialog, DialogContent } from \"@/components/ui/dialog\"\n\nconst Command = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive\n    ref={ref}\n    className={cn(\n      \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\nCommand.displayName = CommandPrimitive.displayName\n\ninterface CommandDialogProps extends DialogProps {}\n\nconst CommandDialog = ({ children, ...props }: CommandDialogProps) => {\n  return (\n    <Dialog {...props}>\n      <DialogContent className=\"overflow-hidden p-0 shadow-2xl\">\n        <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n          {children}\n        </Command>\n      </DialogContent>\n    </Dialog>\n  )\n}\n\nconst CommandInput = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Input>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n  <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n    <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n    <CommandPrimitive.Input\n      ref={ref}\n      className={cn(\n        \"placeholder:text-foreground-muted flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50\",\n        className\n      )}\n      {...props}\n    />\n  </div>\n))\n\nCommandInput.displayName = CommandPrimitive.Input.displayName\n\nconst CommandList = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.List\n    ref={ref}\n    className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n    {...props}\n  />\n))\n\nCommandList.displayName = CommandPrimitive.List.displayName\n\nconst CommandEmpty = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Empty>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n  <CommandPrimitive.Empty\n    ref={ref}\n    className=\"py-6 text-center text-sm\"\n    {...props}\n  />\n))\n\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName\n\nconst CommandGroup = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Group>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Group\n    ref={ref}\n    className={cn(\n      \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\n\nCommandGroup.displayName = CommandPrimitive.Group.displayName\n\nconst CommandSeparator = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 h-px bg-border\", className)}\n    {...props}\n  />\n))\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName\n\nconst CommandItem = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  />\n))\n\nCommandItem.displayName = CommandPrimitive.Item.displayName\n\nconst CommandShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\n        \"ml-auto text-xs tracking-widest text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\nCommandShortcut.displayName = \"CommandShortcut\"\n\nexport {\n  Command,\n  CommandDialog,\n  CommandInput,\n  CommandList,\n  CommandEmpty,\n  CommandGroup,\n  CommandItem,\n  CommandShortcut,\n  CommandSeparator,\n}\n"
  },
  {
    "path": "components/ui/context-menu.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ContextMenuPrimitive from \"@radix-ui/react-context-menu\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst ContextMenu = ContextMenuPrimitive.Root\n\nconst ContextMenuTrigger = ContextMenuPrimitive.Trigger\n\nconst ContextMenuGroup = ContextMenuPrimitive.Group\n\nconst ContextMenuPortal = ContextMenuPrimitive.Portal\n\nconst ContextMenuSub = ContextMenuPrimitive.Sub\n\nconst ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup\n\nconst ContextMenuSubTrigger = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {\n    inset?: boolean\n  }\n>(({ className, inset, children, ...props }, ref) => (\n  <ContextMenuPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <ChevronRight className=\"ml-auto h-4 w-4\" />\n  </ContextMenuPrimitive.SubTrigger>\n))\nContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName\n\nconst ContextMenuSubContent = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.SubContent>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n  <ContextMenuPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in slide-in-from-left-1\",\n      className\n    )}\n    {...props}\n  />\n))\nContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName\n\nconst ContextMenuContent = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <ContextMenuPrimitive.Portal>\n    <ContextMenuPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80\",\n        className\n      )}\n      {...props}\n    />\n  </ContextMenuPrimitive.Portal>\n))\nContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName\n\nconst ContextMenuItem = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <ContextMenuPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName\n\nconst ContextMenuCheckboxItem = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n  <ContextMenuPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <ContextMenuPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </ContextMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </ContextMenuPrimitive.CheckboxItem>\n))\nContextMenuCheckboxItem.displayName =\n  ContextMenuPrimitive.CheckboxItem.displayName\n\nconst ContextMenuRadioItem = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n  <ContextMenuPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <ContextMenuPrimitive.ItemIndicator>\n        <Circle className=\"h-2 w-2 fill-current\" />\n      </ContextMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </ContextMenuPrimitive.RadioItem>\n))\nContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName\n\nconst ContextMenuLabel = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <ContextMenuPrimitive.Label\n    ref={ref}\n    className={cn(\n      \"px-2 py-1.5 text-sm font-semibold text-foreground\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName\n\nconst ContextMenuSeparator = React.forwardRef<\n  React.ElementRef<typeof ContextMenuPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <ContextMenuPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n    {...props}\n  />\n))\nContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName\n\nconst ContextMenuShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\n        \"ml-auto text-xs tracking-widest text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\nContextMenuShortcut.displayName = \"ContextMenuShortcut\"\n\nexport {\n  ContextMenu,\n  ContextMenuTrigger,\n  ContextMenuContent,\n  ContextMenuItem,\n  ContextMenuCheckboxItem,\n  ContextMenuRadioItem,\n  ContextMenuLabel,\n  ContextMenuSeparator,\n  ContextMenuShortcut,\n  ContextMenuGroup,\n  ContextMenuPortal,\n  ContextMenuSub,\n  ContextMenuSubContent,\n  ContextMenuSubTrigger,\n  ContextMenuRadioGroup,\n}\n"
  },
  {
    "path": "components/ui/dialog.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Dialog = DialogPrimitive.Root\n\nconst DialogTrigger = DialogPrimitive.Trigger\n\nconst DialogPortal = ({\n  className,\n  children,\n  ...props\n}: DialogPrimitive.DialogPortalProps) => (\n  <DialogPrimitive.Portal className={cn(className)} {...props}>\n    <div className=\"fixed inset-0 z-50 flex items-start justify-center sm:items-center\">\n      {children}\n    </div>\n  </DialogPrimitive.Portal>\n)\nDialogPortal.displayName = DialogPrimitive.Portal.displayName\n\nconst DialogOverlay = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    ref={ref}\n    className={cn(\n      \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in\",\n      className\n    )}\n    {...props}\n  />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\nconst DialogContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n  <DialogPortal>\n    <DialogOverlay />\n    <DialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n        <X className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Close</span>\n      </DialogPrimitive.Close>\n    </DialogPrimitive.Content>\n  </DialogPortal>\n))\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\nconst DialogHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-1.5 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nDialogHeader.displayName = \"DialogHeader\"\n\nconst DialogFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    ref={ref}\n    className={cn(\n      \"text-lg font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\nexport {\n  Dialog,\n  DialogTrigger,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n}\n"
  },
  {
    "path": "components/ui/dropdown-menu.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n    inset?: boolean\n  }\n>(({ className, inset, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <ChevronRight className=\"ml-auto h-4 w-4\" />\n  </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName =\n  DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"text-on-popover z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuSubContent.displayName =\n  DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n  <DropdownMenuPrimitive.Portal>\n    <DropdownMenuPrimitive.Content\n      ref={ref}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in 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\",\n        className\n      )}\n      {...props}\n    />\n  </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n  <DropdownMenuPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName =\n  DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Circle className=\"h-2 w-2 fill-current\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Label\n    ref={ref}\n    className={cn(\n      \"px-2 py-1.5 text-sm font-semibold\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n    {...props}\n  />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)}\n      {...props}\n    />\n  )\n}\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\"\n\nexport {\n  DropdownMenu,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuCheckboxItem,\n  DropdownMenuRadioItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuGroup,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuRadioGroup,\n}\n"
  },
  {
    "path": "components/ui/hover-card.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as HoverCardPrimitive from \"@radix-ui/react-hover-card\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst HoverCard = HoverCardPrimitive.Root\n\nconst HoverCardTrigger = HoverCardPrimitive.Trigger\n\nconst HoverCardContent = React.forwardRef<\n  React.ElementRef<typeof HoverCardPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n  <HoverCardPrimitive.Content\n    ref={ref}\n    align={align}\n    sideOffset={sideOffset}\n    className={cn(\n      \"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in zoom-in-90\",\n      className\n    )}\n    {...props}\n  />\n))\nHoverCardContent.displayName = HoverCardPrimitive.Content.displayName\n\nexport { HoverCard, HoverCardTrigger, HoverCardContent }\n"
  },
  {
    "path": "components/ui/input.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n  extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, type, ...props }, ref) => {\n    return (\n      <input\n        type={type}\n        className={cn(\n          \"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"
  },
  {
    "path": "components/ui/label.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst labelVariants = cva(\n  \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n)\n\nconst Label = React.forwardRef<\n  React.ElementRef<typeof LabelPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n    VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n  <LabelPrimitive.Root\n    ref={ref}\n    className={cn(labelVariants(), className)}\n    {...props}\n  />\n))\nLabel.displayName = LabelPrimitive.Root.displayName\n\nexport { Label }\n"
  },
  {
    "path": "components/ui/menubar.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as MenubarPrimitive from \"@radix-ui/react-menubar\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst MenubarMenu = MenubarPrimitive.Menu\n\nconst MenubarGroup = MenubarPrimitive.Group\n\nconst MenubarPortal = MenubarPrimitive.Portal\n\nconst MenubarSub = MenubarPrimitive.Sub\n\nconst MenubarRadioGroup = MenubarPrimitive.RadioGroup\n\nconst Menubar = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <MenubarPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"flex h-10 items-center space-x-1 rounded-md border bg-background p-1\",\n      className\n    )}\n    {...props}\n  />\n))\nMenubar.displayName = MenubarPrimitive.Root.displayName\n\nconst MenubarTrigger = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n  <MenubarPrimitive.Trigger\n    ref={ref}\n    className={cn(\n      \"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\nMenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName\n\nconst MenubarSubTrigger = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.SubTrigger>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {\n    inset?: boolean\n  }\n>(({ className, inset, children, ...props }, ref) => (\n  <MenubarPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <ChevronRight className=\"ml-auto h-4 w-4\" />\n  </MenubarPrimitive.SubTrigger>\n))\nMenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName\n\nconst MenubarSubContent = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.SubContent>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n  <MenubarPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1\",\n      className\n    )}\n    {...props}\n  />\n))\nMenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName\n\nconst MenubarContent = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>\n>(\n  (\n    { className, align = \"start\", alignOffset = -4, sideOffset = 8, ...props },\n    ref\n  ) => (\n    <MenubarPrimitive.Portal>\n      <MenubarPrimitive.Content\n        ref={ref}\n        align={align}\n        alignOffset={alignOffset}\n        sideOffset={sideOffset}\n        className={cn(\n          \"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in slide-in-from-top-1\",\n          className\n        )}\n        {...props}\n      />\n    </MenubarPrimitive.Portal>\n  )\n)\nMenubarContent.displayName = MenubarPrimitive.Content.displayName\n\nconst MenubarItem = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <MenubarPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nMenubarItem.displayName = MenubarPrimitive.Item.displayName\n\nconst MenubarCheckboxItem = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n  <MenubarPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <MenubarPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </MenubarPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </MenubarPrimitive.CheckboxItem>\n))\nMenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName\n\nconst MenubarRadioItem = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.RadioItem>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n  <MenubarPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <MenubarPrimitive.ItemIndicator>\n        <Circle className=\"h-2 w-2 fill-current\" />\n      </MenubarPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </MenubarPrimitive.RadioItem>\n))\nMenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName\n\nconst MenubarLabel = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <MenubarPrimitive.Label\n    ref={ref}\n    className={cn(\n      \"px-2 py-1.5 text-sm font-semibold\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nMenubarLabel.displayName = MenubarPrimitive.Label.displayName\n\nconst MenubarSeparator = React.forwardRef<\n  React.ElementRef<typeof MenubarPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <MenubarPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n    {...props}\n  />\n))\nMenubarSeparator.displayName = MenubarPrimitive.Separator.displayName\n\nconst MenubarShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\n        \"ml-auto text-xs tracking-widest text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\nMenubarShortcut.displayname = \"MenubarShortcut\"\n\nexport {\n  Menubar,\n  MenubarMenu,\n  MenubarTrigger,\n  MenubarContent,\n  MenubarItem,\n  MenubarSeparator,\n  MenubarLabel,\n  MenubarCheckboxItem,\n  MenubarRadioGroup,\n  MenubarRadioItem,\n  MenubarPortal,\n  MenubarSubContent,\n  MenubarSubTrigger,\n  MenubarGroup,\n  MenubarSub,\n  MenubarShortcut,\n}\n"
  },
  {
    "path": "components/ui/navigation-menu.tsx",
    "content": "import * as React from \"react\"\nimport * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\"\nimport { cva } from \"class-variance-authority\"\nimport { ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst NavigationMenu = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n  <NavigationMenuPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative z-10 flex flex-1 items-center justify-center\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <NavigationMenuViewport />\n  </NavigationMenuPrimitive.Root>\n))\nNavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName\n\nconst NavigationMenuList = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.List\n    ref={ref}\n    className={cn(\n      \"group flex flex-1 list-none items-center justify-center space-x-1\",\n      className\n    )}\n    {...props}\n  />\n))\nNavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName\n\nconst NavigationMenuItem = NavigationMenuPrimitive.Item\n\nconst navigationMenuTriggerStyle = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:bg-accent focus:text-accent-foreground disabled:opacity-50 disabled:pointer-events-none bg-background hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent/50 data-[active]:bg-accent/50 h-10 py-2 px-4 group w-max\"\n)\n\nconst NavigationMenuTrigger = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n  <NavigationMenuPrimitive.Trigger\n    ref={ref}\n    className={cn(navigationMenuTriggerStyle(), \"group\", className)}\n    {...props}\n  >\n    {children}{\" \"}\n    <ChevronDown\n      className=\"relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180\"\n      aria-hidden=\"true\"\n    />\n  </NavigationMenuPrimitive.Trigger>\n))\nNavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName\n\nconst NavigationMenuContent = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Content\n    ref={ref}\n    className={cn(\n      \"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto \",\n      className\n    )}\n    {...props}\n  />\n))\nNavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName\n\nconst NavigationMenuLink = NavigationMenuPrimitive.Link\n\nconst NavigationMenuViewport = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>\n>(({ className, ...props }, ref) => (\n  <div className={cn(\"absolute left-0 top-full flex justify-center\")}>\n    <NavigationMenuPrimitive.Viewport\n      className={cn(\n        \"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]\",\n        className\n      )}\n      ref={ref}\n      {...props}\n    />\n  </div>\n))\nNavigationMenuViewport.displayName =\n  NavigationMenuPrimitive.Viewport.displayName\n\nconst NavigationMenuIndicator = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Indicator\n    ref={ref}\n    className={cn(\n      \"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in\",\n      className\n    )}\n    {...props}\n  >\n    <div className=\"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md\" />\n  </NavigationMenuPrimitive.Indicator>\n))\nNavigationMenuIndicator.displayName =\n  NavigationMenuPrimitive.Indicator.displayName\n\nexport {\n  navigationMenuTriggerStyle,\n  NavigationMenu,\n  NavigationMenuList,\n  NavigationMenuItem,\n  NavigationMenuContent,\n  NavigationMenuTrigger,\n  NavigationMenuLink,\n  NavigationMenuIndicator,\n  NavigationMenuViewport,\n}\n"
  },
  {
    "path": "components/ui/popover.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Popover = PopoverPrimitive.Root\n\nconst PopoverTrigger = PopoverPrimitive.Trigger\n\nconst PopoverContent = React.forwardRef<\n  React.ElementRef<typeof PopoverPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n  <PopoverPrimitive.Portal>\n    <PopoverPrimitive.Content\n      ref={ref}\n      align={align}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in 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\",\n        className\n      )}\n      {...props}\n    />\n  </PopoverPrimitive.Portal>\n))\nPopoverContent.displayName = PopoverPrimitive.Content.displayName\n\nexport { Popover, PopoverTrigger, PopoverContent }\n"
  },
  {
    "path": "components/ui/progress.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ProgressPrimitive from \"@radix-ui/react-progress\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Progress = React.forwardRef<\n  React.ElementRef<typeof ProgressPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>\n>(({ className, value, ...props }, ref) => (\n  <ProgressPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative h-4 w-full overflow-hidden rounded-full bg-secondary\",\n      className\n    )}\n    {...props}\n  >\n    <ProgressPrimitive.Indicator\n      className=\"h-full w-full flex-1 bg-primary transition-all\"\n      style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n    />\n  </ProgressPrimitive.Root>\n))\nProgress.displayName = ProgressPrimitive.Root.displayName\n\nexport { Progress }\n"
  },
  {
    "path": "components/ui/radio-group.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\"\nimport { Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst RadioGroup = React.forwardRef<\n  React.ElementRef<typeof RadioGroupPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>\n>(({ className, ...props }, ref) => {\n  return (\n    <RadioGroupPrimitive.Root\n      className={cn(\"grid gap-2\", className)}\n      {...props}\n      ref={ref}\n    />\n  )\n})\nRadioGroup.displayName = RadioGroupPrimitive.Root.displayName\n\nconst RadioGroupItem = React.forwardRef<\n  React.ElementRef<typeof RadioGroupPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n>(({ className, children, ...props }, ref) => {\n  return (\n    <RadioGroupPrimitive.Item\n      ref={ref}\n      className={cn(\n        \"h-4 w-4 rounded-full border border-input ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n        className\n      )}\n      {...props}\n    >\n      <RadioGroupPrimitive.Indicator className=\"flex items-center justify-center\">\n        <Circle className=\"h-2.5 w-2.5 fill-primary text-primary\" />\n      </RadioGroupPrimitive.Indicator>\n    </RadioGroupPrimitive.Item>\n  )\n})\nRadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName\n\nexport { RadioGroup, RadioGroupItem }\n"
  },
  {
    "path": "components/ui/scroll-area.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst ScrollArea = React.forwardRef<\n  React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n  <ScrollAreaPrimitive.Root\n    ref={ref}\n    className={cn(\"relative overflow-hidden\", className)}\n    {...props}\n  >\n    <ScrollAreaPrimitive.Viewport className=\"h-full w-full rounded-[inherit]\">\n      {children}\n    </ScrollAreaPrimitive.Viewport>\n    <ScrollBar />\n    <ScrollAreaPrimitive.Corner />\n  </ScrollAreaPrimitive.Root>\n))\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName\n\nconst ScrollBar = React.forwardRef<\n  React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = \"vertical\", ...props }, ref) => (\n  <ScrollAreaPrimitive.ScrollAreaScrollbar\n    ref={ref}\n    orientation={orientation}\n    className={cn(\n      \"flex touch-none select-none transition-colors\",\n      orientation === \"vertical\" &&\n        \"h-full w-2.5 border-l border-l-transparent p-[1px]\",\n      orientation === \"horizontal\" &&\n        \"h-2.5 border-t border-t-transparent p-[1px]\",\n      className\n    )}\n    {...props}\n  >\n    <ScrollAreaPrimitive.ScrollAreaThumb className=\"relative flex-1 rounded-full bg-border\" />\n  </ScrollAreaPrimitive.ScrollAreaScrollbar>\n))\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName\n\nexport { ScrollArea, ScrollBar }\n"
  },
  {
    "path": "components/ui/select.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { Check, ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nconst SelectTrigger = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n  <SelectPrimitive.Trigger\n    ref={ref}\n    className={cn(\n      \"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <SelectPrimitive.Icon asChild>\n      <ChevronDown className=\"h-4 w-4 opacity-50\" />\n    </SelectPrimitive.Icon>\n  </SelectPrimitive.Trigger>\n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectContent = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n  <SelectPrimitive.Portal>\n    <SelectPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-80\",\n        position === \"popper\" && \"translate-y-1\",\n        className\n      )}\n      position={position}\n      {...props}\n    >\n      <SelectPrimitive.Viewport\n        className={cn(\n          \"p-1\",\n          position === \"popper\" &&\n            \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n        )}\n      >\n        {children}\n      </SelectPrimitive.Viewport>\n    </SelectPrimitive.Content>\n  </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n  <SelectPrimitive.Label\n    ref={ref}\n    className={cn(\"py-1.5 pl-8 pr-2 text-sm font-semibold\", className)}\n    {...props}\n  />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n  <SelectPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <SelectPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </SelectPrimitive.ItemIndicator>\n    </span>\n\n    <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n  </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <SelectPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n    {...props}\n  />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nexport {\n  Select,\n  SelectGroup,\n  SelectValue,\n  SelectTrigger,\n  SelectContent,\n  SelectLabel,\n  SelectItem,\n  SelectSeparator,\n}\n"
  },
  {
    "path": "components/ui/separator.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Separator = React.forwardRef<\n  React.ElementRef<typeof SeparatorPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n  (\n    { className, orientation = \"horizontal\", decorative = true, ...props },\n    ref\n  ) => (\n    <SeparatorPrimitive.Root\n      ref={ref}\n      decorative={decorative}\n      orientation={orientation}\n      className={cn(\n        \"shrink-0 bg-border\",\n        orientation === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",\n        className\n      )}\n      {...props}\n    />\n  )\n)\nSeparator.displayName = SeparatorPrimitive.Root.displayName\n\nexport { Separator }\n"
  },
  {
    "path": "components/ui/sheet.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { VariantProps, cva } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Sheet = SheetPrimitive.Root\n\nconst SheetTrigger = SheetPrimitive.Trigger\n\nconst portalVariants = cva(\"fixed inset-0 z-50 flex\", {\n  variants: {\n    position: {\n      top: \"items-start\",\n      bottom: \"items-end\",\n      left: \"justify-start\",\n      right: \"justify-end\",\n    },\n  },\n  defaultVariants: { position: \"right\" },\n})\n\ninterface SheetPortalProps\n  extends SheetPrimitive.DialogPortalProps,\n    VariantProps<typeof portalVariants> {}\n\nconst SheetPortal = ({\n  position,\n  className,\n  children,\n  ...props\n}: SheetPortalProps) => (\n  <SheetPrimitive.Portal className={cn(className)} {...props}>\n    <div className={portalVariants({ position })}>{children}</div>\n  </SheetPrimitive.Portal>\n)\nSheetPortal.displayName = SheetPrimitive.Portal.displayName\n\nconst SheetOverlay = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, children, ...props }, ref) => (\n  <SheetPrimitive.Overlay\n    className={cn(\n      \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\nconst sheetVariants = cva(\n  \"fixed z-50 scale-100 gap-4 bg-background p-6 opacity-100 shadow-lg border\",\n  {\n    variants: {\n      position: {\n        top: \"animate-in slide-in-from-top w-full duration-300\",\n        bottom: \"animate-in slide-in-from-bottom w-full duration-300\",\n        left: \"animate-in slide-in-from-left h-full duration-300\",\n        right: \"animate-in slide-in-from-right h-full duration-300\",\n      },\n      size: {\n        content: \"\",\n        default: \"\",\n        sm: \"\",\n        lg: \"\",\n        xl: \"\",\n        full: \"\",\n      },\n    },\n    compoundVariants: [\n      {\n        position: [\"top\", \"bottom\"],\n        size: \"content\",\n        class: \"max-h-screen\",\n      },\n      {\n        position: [\"top\", \"bottom\"],\n        size: \"default\",\n        class: \"h-1/3\",\n      },\n      {\n        position: [\"top\", \"bottom\"],\n        size: \"sm\",\n        class: \"h-1/4\",\n      },\n      {\n        position: [\"top\", \"bottom\"],\n        size: \"lg\",\n        class: \"h-1/2\",\n      },\n      {\n        position: [\"top\", \"bottom\"],\n        size: \"xl\",\n        class: \"h-5/6\",\n      },\n      {\n        position: [\"top\", \"bottom\"],\n        size: \"full\",\n        class: \"h-screen\",\n      },\n      {\n        position: [\"right\", \"left\"],\n        size: \"content\",\n        class: \"max-w-screen\",\n      },\n      {\n        position: [\"right\", \"left\"],\n        size: \"default\",\n        class: \"w-1/3\",\n      },\n      {\n        position: [\"right\", \"left\"],\n        size: \"sm\",\n        class: \"w-1/4\",\n      },\n      {\n        position: [\"right\", \"left\"],\n        size: \"lg\",\n        class: \"w-1/2\",\n      },\n      {\n        position: [\"right\", \"left\"],\n        size: \"xl\",\n        class: \"w-5/6\",\n      },\n      {\n        position: [\"right\", \"left\"],\n        size: \"full\",\n        class: \"w-screen\",\n      },\n    ],\n    defaultVariants: {\n      position: \"right\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface DialogContentProps\n  extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n    VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Content>,\n  DialogContentProps\n>(({ position, size, className, children, ...props }, ref) => (\n  <SheetPortal position={position}>\n    <SheetOverlay />\n    <SheetPrimitive.Content\n      ref={ref}\n      className={cn(sheetVariants({ position, size }), className)}\n      {...props}\n    >\n      {children}\n      <SheetPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary\">\n        <X className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Close</span>\n      </SheetPrimitive.Close>\n    </SheetPrimitive.Content>\n  </SheetPortal>\n))\nSheetContent.displayName = SheetPrimitive.Content.displayName\n\nconst SheetHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-2 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nSheetHeader.displayName = \"SheetHeader\"\n\nconst SheetFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nSheetFooter.displayName = \"SheetFooter\"\n\nconst SheetTitle = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Title\n    ref={ref}\n    className={cn(\"text-lg font-semibold text-foreground\", className)}\n    {...props}\n  />\n))\nSheetTitle.displayName = SheetPrimitive.Title.displayName\n\nconst SheetDescription = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nSheetDescription.displayName = SheetPrimitive.Description.displayName\n\nexport {\n  Sheet,\n  SheetTrigger,\n  SheetContent,\n  SheetHeader,\n  SheetFooter,\n  SheetTitle,\n  SheetDescription,\n}\n"
  },
  {
    "path": "components/ui/skeleton.tsx",
    "content": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n  return (\n    <div\n      className={cn(\"animate-pulse rounded-md bg-muted\", className)}\n      {...props}\n    />\n  )\n}\n\nexport { Skeleton }\n"
  },
  {
    "path": "components/ui/slider.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SliderPrimitive from \"@radix-ui/react-slider\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Slider = React.forwardRef<\n  React.ElementRef<typeof SliderPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <SliderPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative flex w-full touch-none select-none items-center\",\n      className\n    )}\n    {...props}\n  >\n    <SliderPrimitive.Track className=\"relative h-2 w-full grow overflow-hidden rounded-full bg-secondary\">\n      <SliderPrimitive.Range className=\"absolute h-full bg-primary\" />\n    </SliderPrimitive.Track>\n    <SliderPrimitive.Thumb className=\"block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\" />\n  </SliderPrimitive.Root>\n))\nSlider.displayName = SliderPrimitive.Root.displayName\n\nexport { Slider }\n"
  },
  {
    "path": "components/ui/switch.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Switch = React.forwardRef<\n  React.ElementRef<typeof SwitchPrimitives.Root>,\n  React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>\n>(({ className, ...props }, ref) => (\n  <SwitchPrimitives.Root\n    className={cn(\n      \"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  >\n    <SwitchPrimitives.Thumb\n      className={cn(\n        \"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n      )}\n    />\n  </SwitchPrimitives.Root>\n))\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n"
  },
  {
    "path": "components/ui/tabs.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Tabs = TabsPrimitive.Root\n\nconst TabsList = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.List\n    ref={ref}\n    className={cn(\n      \"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\nTabsList.displayName = TabsPrimitive.List.displayName\n\nconst TabsTrigger = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Trigger\n    ref={ref}\n    className={cn(\n      \"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm\",\n      className\n    )}\n    {...props}\n  />\n))\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName\n\nconst TabsContent = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Content\n    ref={ref}\n    className={cn(\n      \"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n      className\n    )}\n    {...props}\n  />\n))\nTabsContent.displayName = TabsPrimitive.Content.displayName\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent }\n"
  },
  {
    "path": "components/ui/textarea.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface TextareaProps\n  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n  ({ className, ...props }, ref) => {\n    return (\n      <textarea\n        className={cn(\n          \"flex h-20 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nTextarea.displayName = \"Textarea\"\n\nexport { Textarea }\n"
  },
  {
    "path": "components/ui/toast.tsx",
    "content": "import * as React from \"react\"\nimport * as ToastPrimitives from \"@radix-ui/react-toast\"\nimport { VariantProps, cva } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst ToastProvider = ToastPrimitives.Provider\n\nconst ToastViewport = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Viewport>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Viewport\n    ref={ref}\n    className={cn(\n      \"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]\",\n      className\n    )}\n    {...props}\n  />\n))\nToastViewport.displayName = ToastPrimitives.Viewport.displayName\n\nconst toastVariants = cva(\n  \"data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-background border\",\n        destructive:\n          \"group destructive border-destructive bg-destructive text-destructive-foreground\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst Toast = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Root>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &\n    VariantProps<typeof toastVariants>\n>(({ className, variant, ...props }, ref) => {\n  return (\n    <ToastPrimitives.Root\n      ref={ref}\n      className={cn(toastVariants({ variant }), className)}\n      {...props}\n    />\n  )\n})\nToast.displayName = ToastPrimitives.Root.displayName\n\nconst ToastAction = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Action>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Action\n    ref={ref}\n    className={cn(\n      \"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-destructive/30 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive\",\n      className\n    )}\n    {...props}\n  />\n))\nToastAction.displayName = ToastPrimitives.Action.displayName\n\nconst ToastClose = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Close>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Close\n    ref={ref}\n    className={cn(\n      \"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600\",\n      className\n    )}\n    toast-close=\"\"\n    {...props}\n  >\n    <X className=\"h-4 w-4\" />\n  </ToastPrimitives.Close>\n))\nToastClose.displayName = ToastPrimitives.Close.displayName\n\nconst ToastTitle = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Title>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Title\n    ref={ref}\n    className={cn(\"text-sm font-semibold\", className)}\n    {...props}\n  />\n))\nToastTitle.displayName = ToastPrimitives.Title.displayName\n\nconst ToastDescription = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Description>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Description\n    ref={ref}\n    className={cn(\"text-sm opacity-90\", className)}\n    {...props}\n  />\n))\nToastDescription.displayName = ToastPrimitives.Description.displayName\n\ntype ToastProps = React.ComponentPropsWithoutRef<typeof Toast>\n\ntype ToastActionElement = React.ReactElement<typeof ToastAction>\n\nexport {\n  type ToastProps,\n  type ToastActionElement,\n  ToastProvider,\n  ToastViewport,\n  Toast,\n  ToastTitle,\n  ToastDescription,\n  ToastClose,\n  ToastAction,\n}\n"
  },
  {
    "path": "components/ui/toaster.tsx",
    "content": "\"use client\"\n\nimport {\n  Toast,\n  ToastClose,\n  ToastDescription,\n  ToastProvider,\n  ToastTitle,\n  ToastViewport,\n} from \"@/components/ui/toast\"\nimport { useToast } from \"@/components/ui/use-toast\"\n\nexport function Toaster() {\n  const { toasts } = useToast()\n\n  return (\n    <ToastProvider>\n      {toasts.map(function ({ id, title, description, action, ...props }) {\n        return (\n          <Toast key={id} {...props}>\n            <div className=\"grid gap-1\">\n              {title && <ToastTitle>{title}</ToastTitle>}\n              {description && (\n                <ToastDescription>{description}</ToastDescription>\n              )}\n            </div>\n            {action}\n            <ToastClose />\n          </Toast>\n        )\n      })}\n      <ToastViewport />\n    </ToastProvider>\n  )\n}\n"
  },
  {
    "path": "components/ui/toggle.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\"\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst toggleVariants = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors data-[state=on]:bg-accent data-[state=on]:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background hover:bg-muted hover:text-muted-foreground\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        outline:\n          \"bg-transparent border border-input hover:bg-accent hover:text-accent-foreground\",\n      },\n      size: {\n        default: \"h-10 px-3\",\n        sm: \"h-9 px-2.5\",\n        lg: \"h-11 px-5\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nconst Toggle = React.forwardRef<\n  React.ElementRef<typeof TogglePrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n    VariantProps<typeof toggleVariants>\n>(({ className, variant, size, ...props }, ref) => (\n  <TogglePrimitive.Root\n    ref={ref}\n    className={cn(toggleVariants({ variant, size, className }))}\n    {...props}\n  />\n))\n\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport { Toggle, toggleVariants }\n"
  },
  {
    "path": "components/ui/tooltip.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst TooltipProvider = TooltipPrimitive.Provider\n\nconst Tooltip = TooltipPrimitive.Root\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\nconst TooltipContent = React.forwardRef<\n  React.ElementRef<typeof TooltipPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n  <TooltipPrimitive.Content\n    ref={ref}\n    sideOffset={sideOffset}\n    className={cn(\n      \"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1\",\n      className\n    )}\n    {...props}\n  />\n))\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n"
  },
  {
    "path": "components/ui/use-toast.ts",
    "content": "// Inspired by react-hot-toast library\nimport * as React from \"react\"\n\nimport { ToastActionElement, type ToastProps } from \"@/components/ui/toast\"\n\nconst TOAST_LIMIT = 1\nconst TOAST_REMOVE_DELAY = 1000000\n\ntype ToasterToast = ToastProps & {\n  id: string\n  title?: React.ReactNode\n  description?: React.ReactNode\n  action?: ToastActionElement\n}\n\nconst actionTypes = {\n  ADD_TOAST: \"ADD_TOAST\",\n  UPDATE_TOAST: \"UPDATE_TOAST\",\n  DISMISS_TOAST: \"DISMISS_TOAST\",\n  REMOVE_TOAST: \"REMOVE_TOAST\",\n} as const\n\nlet count = 0\n\nfunction genId() {\n  count = (count + 1) % Number.MAX_VALUE\n  return count.toString()\n}\n\ntype ActionType = typeof actionTypes\n\ntype Action =\n  | {\n      type: ActionType[\"ADD_TOAST\"]\n      toast: ToasterToast\n    }\n  | {\n      type: ActionType[\"UPDATE_TOAST\"]\n      toast: Partial<ToasterToast>\n    }\n  | {\n      type: ActionType[\"DISMISS_TOAST\"]\n      toastId?: ToasterToast[\"id\"]\n    }\n  | {\n      type: ActionType[\"REMOVE_TOAST\"]\n      toastId?: ToasterToast[\"id\"]\n    }\n\ninterface State {\n  toasts: ToasterToast[]\n}\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n\nconst addToRemoveQueue = (toastId: string) => {\n  if (toastTimeouts.has(toastId)) {\n    return\n  }\n\n  const timeout = setTimeout(() => {\n    toastTimeouts.delete(toastId)\n    dispatch({\n      type: \"REMOVE_TOAST\",\n      toastId: toastId,\n    })\n  }, TOAST_REMOVE_DELAY)\n\n  toastTimeouts.set(toastId, timeout)\n}\n\nexport const reducer = (state: State, action: Action): State => {\n  switch (action.type) {\n    case \"ADD_TOAST\":\n      return {\n        ...state,\n        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),\n      }\n\n    case \"UPDATE_TOAST\":\n      return {\n        ...state,\n        toasts: state.toasts.map((t) =>\n          t.id === action.toast.id ? { ...t, ...action.toast } : t\n        ),\n      }\n\n    case \"DISMISS_TOAST\": {\n      const { toastId } = action\n\n      // ! Side effects ! - This could be extracted into a dismissToast() action,\n      // but I'll keep it here for simplicity\n      if (toastId) {\n        addToRemoveQueue(toastId)\n      } else {\n        state.toasts.forEach((toast) => {\n          addToRemoveQueue(toast.id)\n        })\n      }\n\n      return {\n        ...state,\n        toasts: state.toasts.map((t) =>\n          t.id === toastId || toastId === undefined\n            ? {\n                ...t,\n                open: false,\n              }\n            : t\n        ),\n      }\n    }\n    case \"REMOVE_TOAST\":\n      if (action.toastId === undefined) {\n        return {\n          ...state,\n          toasts: [],\n        }\n      }\n      return {\n        ...state,\n        toasts: state.toasts.filter((t) => t.id !== action.toastId),\n      }\n  }\n}\n\nconst listeners: Array<(state: State) => void> = []\n\nlet memoryState: State = { toasts: [] }\n\nfunction dispatch(action: Action) {\n  memoryState = reducer(memoryState, action)\n  listeners.forEach((listener) => {\n    listener(memoryState)\n  })\n}\n\ninterface Toast extends Omit<ToasterToast, \"id\"> {}\n\nfunction toast({ ...props }: Toast) {\n  const id = genId()\n\n  const update = (props: ToasterToast) =>\n    dispatch({\n      type: \"UPDATE_TOAST\",\n      toast: { ...props, id },\n    })\n  const dismiss = () => dispatch({ type: \"DISMISS_TOAST\", toastId: id })\n\n  dispatch({\n    type: \"ADD_TOAST\",\n    toast: {\n      ...props,\n      id,\n      open: true,\n      onOpenChange: (open) => {\n        if (!open) dismiss()\n      },\n    },\n  })\n\n  return {\n    id: id,\n    dismiss,\n    update,\n  }\n}\n\nfunction useToast() {\n  const [state, setState] = React.useState<State>(memoryState)\n\n  React.useEffect(() => {\n    listeners.push(setState)\n    return () => {\n      const index = listeners.indexOf(setState)\n      if (index > -1) {\n        listeners.splice(index, 1)\n      }\n    }\n  }, [state])\n\n  return {\n    ...state,\n    toast,\n    dismiss: (toastId?: string) => dispatch({ type: \"DISMISS_TOAST\", toastId }),\n  }\n}\n\nexport { useToast, toast }\n"
  },
  {
    "path": "components/user-account-nav.tsx",
    "content": "\"use client\"\n\nimport Link from \"next/link\"\nimport { User } from \"next-auth\"\nimport { signOut } from \"next-auth/react\"\n\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { UserAvatar } from \"@/components/user-avatar\"\n\ninterface UserAccountNavProps extends React.HTMLAttributes<HTMLDivElement> {\n  user: Pick<User, \"name\" | \"image\" | \"email\">\n}\n\nexport function UserAccountNav({ user }: UserAccountNavProps) {\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger>\n        <UserAvatar\n          user={{ name: user.name || null, image: user.image || null }}\n          className=\"h-8 w-8\"\n        />\n      </DropdownMenuTrigger>\n      <DropdownMenuContent align=\"end\">\n        <div className=\"flex items-center justify-start gap-2 p-2\">\n          <div className=\"flex flex-col space-y-1 leading-none\">\n            {user.name && <p className=\"font-medium\">{user.name}</p>}\n            {user.email && (\n              <p className=\"w-[200px] truncate text-sm text-muted-foreground\">\n                {user.email}\n              </p>\n            )}\n          </div>\n        </div>\n        <DropdownMenuSeparator />\n        <DropdownMenuItem asChild>\n          <Link href=\"/dashboard\">Dashboard</Link>\n        </DropdownMenuItem>\n        <DropdownMenuItem asChild>\n          <Link href=\"/dashboard/billing\">Billing</Link>\n        </DropdownMenuItem>\n        <DropdownMenuItem asChild>\n          <Link href=\"/dashboard/settings\">Settings</Link>\n        </DropdownMenuItem>\n        <DropdownMenuSeparator />\n        <DropdownMenuItem\n          className=\"cursor-pointer\"\n          onSelect={(event) => {\n            event.preventDefault()\n            signOut({\n              callbackUrl: `${window.location.origin}/login`,\n            })\n          }}\n        >\n          Sign out\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n"
  },
  {
    "path": "components/user-auth-form.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { useSearchParams } from \"next/navigation\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { signIn } from \"next-auth/react\"\nimport { useForm } from \"react-hook-form\"\nimport * as z from \"zod\"\n\nimport { cn } from \"@/lib/utils\"\nimport { userAuthSchema } from \"@/lib/validations/auth\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { toast } from \"@/components/ui/use-toast\"\nimport { Icons } from \"@/components/icons\"\n\ninterface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ntype FormData = z.infer<typeof userAuthSchema>\n\nexport function UserAuthForm({ className, ...props }: UserAuthFormProps) {\n  const {\n    register,\n    handleSubmit,\n    formState: { errors },\n  } = useForm<FormData>({\n    resolver: zodResolver(userAuthSchema),\n  })\n  const [isLoading, setIsLoading] = React.useState<boolean>(false)\n  const [isGitHubLoading, setIsGitHubLoading] = React.useState<boolean>(false)\n  const searchParams = useSearchParams()\n\n  async function onSubmit(data: FormData) {\n    setIsLoading(true)\n\n    const signInResult = await signIn(\"email\", {\n      email: data.email.toLowerCase(),\n      redirect: false,\n      callbackUrl: searchParams?.get(\"from\") || \"/dashboard\",\n    })\n\n    setIsLoading(false)\n\n    if (!signInResult?.ok) {\n      return toast({\n        title: \"Something went wrong.\",\n        description: \"Your sign in request failed. Please try again.\",\n        variant: \"destructive\",\n      })\n    }\n\n    return toast({\n      title: \"Check your email\",\n      description: \"We sent you a login link. Be sure to check your spam too.\",\n    })\n  }\n\n  return (\n    <div className={cn(\"grid gap-6\", className)} {...props}>\n      <form onSubmit={handleSubmit(onSubmit)}>\n        <div className=\"grid gap-2\">\n          <div className=\"grid gap-1\">\n            <Label className=\"sr-only\" htmlFor=\"email\">\n              Email\n            </Label>\n            <Input\n              id=\"email\"\n              placeholder=\"name@example.com\"\n              type=\"email\"\n              autoCapitalize=\"none\"\n              autoComplete=\"email\"\n              autoCorrect=\"off\"\n              disabled={isLoading || isGitHubLoading}\n              {...register(\"email\")}\n            />\n            {errors?.email && (\n              <p className=\"px-1 text-xs text-red-600\">\n                {errors.email.message}\n              </p>\n            )}\n          </div>\n          <button className={cn(buttonVariants())} disabled={isLoading}>\n            {isLoading && (\n              <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n            )}\n            Sign In with Email\n          </button>\n        </div>\n      </form>\n      <div className=\"relative\">\n        <div className=\"absolute inset-0 flex items-center\">\n          <span className=\"w-full border-t\" />\n        </div>\n        <div className=\"relative flex justify-center text-xs uppercase\">\n          <span className=\"bg-background px-2 text-muted-foreground\">\n            Or continue with\n          </span>\n        </div>\n      </div>\n      <button\n        type=\"button\"\n        className={cn(buttonVariants({ variant: \"outline\" }))}\n        onClick={() => {\n          setIsGitHubLoading(true)\n          signIn(\"github\")\n        }}\n        disabled={isLoading || isGitHubLoading}\n      >\n        {isGitHubLoading ? (\n          <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n        ) : (\n          <Icons.gitHub className=\"mr-2 h-4 w-4\" />\n        )}{\" \"}\n        Github\n      </button>\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/user-avatar.tsx",
    "content": "import { User } from \"@prisma/client\"\nimport { AvatarProps } from \"@radix-ui/react-avatar\"\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Icons } from \"@/components/icons\"\n\ninterface UserAvatarProps extends AvatarProps {\n  user: Pick<User, \"image\" | \"name\">\n}\n\nexport function UserAvatar({ user, ...props }: UserAvatarProps) {\n  return (\n    <Avatar {...props}>\n      {user.image ? (\n        <AvatarImage alt=\"Picture\" src={user.image} />\n      ) : (\n        <AvatarFallback>\n          <span className=\"sr-only\">{user.name}</span>\n          <Icons.user className=\"h-4 w-4\" />\n        </AvatarFallback>\n      )}\n    </Avatar>\n  )\n}\n"
  },
  {
    "path": "components/user-name-form.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { useRouter } from \"next/navigation\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { User } from \"@prisma/client\"\nimport { useForm } from \"react-hook-form\"\nimport * as z from \"zod\"\n\nimport { cn } from \"@/lib/utils\"\nimport { userNameSchema } from \"@/lib/validations/user\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { toast } from \"@/components/ui/use-toast\"\nimport { Icons } from \"@/components/icons\"\n\ninterface UserNameFormProps extends React.HTMLAttributes<HTMLFormElement> {\n  user: Pick<User, \"id\" | \"name\">\n}\n\ntype FormData = z.infer<typeof userNameSchema>\n\nexport function UserNameForm({ user, className, ...props }: UserNameFormProps) {\n  const router = useRouter()\n  const {\n    handleSubmit,\n    register,\n    formState: { errors },\n  } = useForm<FormData>({\n    resolver: zodResolver(userNameSchema),\n    defaultValues: {\n      name: user?.name || \"\",\n    },\n  })\n  const [isSaving, setIsSaving] = React.useState<boolean>(false)\n\n  async function onSubmit(data: FormData) {\n    setIsSaving(true)\n\n    const response = await fetch(`/api/users/${user.id}`, {\n      method: \"PATCH\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        name: data.name,\n      }),\n    })\n\n    setIsSaving(false)\n\n    if (!response?.ok) {\n      return toast({\n        title: \"Something went wrong.\",\n        description: \"Your name was not updated. Please try again.\",\n        variant: \"destructive\",\n      })\n    }\n\n    toast({\n      description: \"Your name has been updated.\",\n    })\n\n    router.refresh()\n  }\n\n  return (\n    <form\n      className={cn(className)}\n      onSubmit={handleSubmit(onSubmit)}\n      {...props}\n    >\n      <Card>\n        <CardHeader>\n          <CardTitle>Your Name</CardTitle>\n          <CardDescription>\n            Please enter your full name or a display name you are comfortable\n            with.\n          </CardDescription>\n        </CardHeader>\n        <CardContent>\n          <div className=\"grid gap-1\">\n            <Label className=\"sr-only\" htmlFor=\"name\">\n              Name\n            </Label>\n            <Input\n              id=\"name\"\n              className=\"w-[400px]\"\n              size={32}\n              {...register(\"name\")}\n            />\n            {errors?.name && (\n              <p className=\"px-1 text-xs text-red-600\">{errors.name.message}</p>\n            )}\n          </div>\n        </CardContent>\n        <CardFooter>\n          <button\n            type=\"submit\"\n            className={cn(buttonVariants(), className)}\n            disabled={isSaving}\n          >\n            {isSaving && (\n              <Icons.spinner className=\"mr-2 h-4 w-4 animate-spin\" />\n            )}\n            <span>Save</span>\n          </button>\n        </CardFooter>\n      </Card>\n    </form>\n  )\n}\n"
  },
  {
    "path": "config/dashboard.ts",
    "content": "import { DashboardConfig } from \"types\"\n\nexport const dashboardConfig: DashboardConfig = {\n  mainNav: [\n    {\n      title: \"Documentation\",\n      href: \"/docs\",\n    },\n    {\n      title: \"Support\",\n      href: \"/support\",\n      disabled: true,\n    },\n  ],\n  sidebarNav: [\n    {\n      title: \"Posts\",\n      href: \"/dashboard\",\n      icon: \"post\",\n    },\n    {\n      title: \"Billing\",\n      href: \"/dashboard/billing\",\n      icon: \"billing\",\n    },\n    {\n      title: \"Settings\",\n      href: \"/dashboard/settings\",\n      icon: \"settings\",\n    },\n  ],\n}\n"
  },
  {
    "path": "config/docs.ts",
    "content": "import { DocsConfig } from \"types\"\n\nexport const docsConfig: DocsConfig = {\n  mainNav: [\n    {\n      title: \"Documentation\",\n      href: \"/docs\",\n    },\n    {\n      title: \"Guides\",\n      href: \"/guides\",\n    },\n  ],\n  sidebarNav: [\n    {\n      title: \"Getting Started\",\n      items: [\n        {\n          title: \"Introduction\",\n          href: \"/docs\",\n        },\n      ],\n    },\n    {\n      title: \"Documentation\",\n      items: [\n        {\n          title: \"Introduction\",\n          href: \"/docs/documentation\",\n        },\n        {\n          title: \"Contentlayer\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Components\",\n          href: \"/docs/documentation/components\",\n        },\n        {\n          title: \"Code Blocks\",\n          href: \"/docs/documentation/code-blocks\",\n        },\n        {\n          title: \"Style Guide\",\n          href: \"/docs/documentation/style-guide\",\n        },\n        {\n          title: \"Search\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n      ],\n    },\n    {\n      title: \"Blog\",\n      items: [\n        {\n          title: \"Introduction\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Build your own\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Writing Posts\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n      ],\n    },\n    {\n      title: \"Dashboard\",\n      items: [\n        {\n          title: \"Introduction\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Layouts\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Server Components\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Authentication\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Database with Prisma\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"API Routes\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n      ],\n    },\n    {\n      title: \"Marketing Site\",\n      items: [\n        {\n          title: \"Introduction\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"File Structure\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Tailwind CSS\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n        {\n          title: \"Typography\",\n          href: \"/docs/in-progress\",\n          disabled: true,\n        },\n      ],\n    },\n  ],\n}\n"
  },
  {
    "path": "config/marketing.ts",
    "content": "import { MarketingConfig } from \"types\"\n\nexport const marketingConfig: MarketingConfig = {\n  mainNav: [\n    {\n      title: \"Features\",\n      href: \"/#features\",\n    },\n    {\n      title: \"Pricing\",\n      href: \"/pricing\",\n    },\n    {\n      title: \"Blog\",\n      href: \"/blog\",\n    },\n    {\n      title: \"Documentation\",\n      href: \"/docs\",\n    },\n  ],\n}\n"
  },
  {
    "path": "config/site.ts",
    "content": "import { SiteConfig } from \"types\"\n\nexport const siteConfig: SiteConfig = {\n  name: \"Taxonomy\",\n  description:\n    \"An open source application built using the new router, server components and everything new in Next.js 13.\",\n  url: \"https://tx.shadcn.com\",\n  ogImage: \"https://tx.shadcn.com/og.jpg\",\n  links: {\n    twitter: \"https://twitter.com/shadcn\",\n    github: \"https://github.com/shadcn/taxonomy\",\n  },\n}\n"
  },
  {
    "path": "config/subscriptions.ts",
    "content": "import { SubscriptionPlan } from \"types\"\nimport { env } from \"@/env.mjs\"\n\nexport const freePlan: SubscriptionPlan = {\n  name: \"Free\",\n  description:\n    \"The free plan is limited to 3 posts. Upgrade to the PRO plan for unlimited posts.\",\n  stripePriceId: \"\",\n}\n\nexport const proPlan: SubscriptionPlan = {\n  name: \"PRO\",\n  description: \"The PRO plan has unlimited posts.\",\n  stripePriceId: env.STRIPE_PRO_MONTHLY_PLAN_ID || \"\",\n}\n"
  },
  {
    "path": "content/authors/shadcn.mdx",
    "content": "---\ntitle: shadcn\navatar: /images/avatars/shadcn.png\ntwitter: shadcn\n---\n"
  },
  {
    "path": "content/blog/deploying-next-apps.mdx",
    "content": "---\ntitle: Deploying Next.js Apps\ndescription: How to deploy your Next.js apps on Vercel.\nimage: /images/blog/blog-post-3.jpg\ndate: \"2023-01-02\"\nauthors:\n  - shadcn\n---\n\n<Callout>\n  The text below is from the [Tailwind\n  CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it\n  here to test the markdown styles. **Tailwind is awesome. You should use it.**\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/blog/dynamic-routing-static-regeneration.mdx",
    "content": "---\ntitle: Dynamic Routing and Static Regeneration\ndescription: How to use incremental static regeneration using dynamic routes.\nimage: /images/blog/blog-post-2.jpg\ndate: \"2023-03-04\"\nauthors:\n  - shadcn\n---\n\n<Callout>\n  The text below is from the [Tailwind\n  CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it\n  here to test the markdown styles. **Tailwind is awesome. You should use it.**\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/blog/preview-mode-headless-cms.mdx",
    "content": "---\ntitle: Preview Mode for Headless CMS\ndescription: How to implement preview mode in your headless CMS.\ndate: \"2023-04-09\"\nimage: /images/blog/blog-post-1.jpg\nauthors:\n  - shadcn\n---\n\n<Callout>\n  The text below is from the [Tailwind\n  CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it\n  here to test the markdown styles. **Tailwind is awesome. You should use it.**\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/blog/server-client-components.mdx",
    "content": "---\ntitle: Server and Client Components\ndescription: React Server Components allow developers to build applications that span the server and client.\nimage: /images/blog/blog-post-4.jpg\ndate: \"2023-01-08\"\nauthors:\n  - shadcn\n---\n\n<Callout>\n  The text below is from the [Tailwind\n  CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it\n  here to test the markdown styles. **Tailwind is awesome. You should use it.**\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/docs/documentation/code-blocks.mdx",
    "content": "---\ntitle: Code Blocks\ndescription: Advanced code blocks with highlighting, file names and more.\n---\n\nThe code blocks on the documentation site and the blog are powered by [rehype-pretty-code](https://github.com/atomiks/rehype-pretty-code). The syntax highlighting is done using [shiki](https://github.com/shikijs/shiki).\n\nIt has the following features:\n\n1. Beautiful code blocks with syntax highlighting.\n2. Support for VS Code themes.\n3. Works with hundreds of languages.\n4. Line and word highlighting.\n5. Support for line numbers.\n6. Show code block titles using meta strings.\n\n<Callout>\n\nThanks to Shiki, highlighting is done at build time. No JavaScript is sent to the client for runtime highlighting.\n\n</Callout>\n\n## Example\n\n```ts showLineNumbers title=\"next.config.js\" {3} /appDir: true/\nimport { withContentlayer } from \"next-contentlayer\"\n\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  reactStrictMode: true,\n  images: {\n    domains: [\"avatars.githubusercontent.com\"],\n  },\n  experimental: {\n    appDir: true,\n    serverComponentsExternalPackages: [\"@prisma/client\"],\n  },\n}\n\nexport default withContentlayer(nextConfig)\n```\n\n## Title\n\n````mdx\n```ts title=\"path/to/file.ts\"\n// Code here\n```\n````\n\n## Line Highlight\n\n````mdx\n```ts {1,3-6}\n// Highlight line 1 and line 3 to 6\n```\n````\n\n## Word Highlight\n\n````mdx\n```ts /shadcn/\n// Highlight the word shadcn.\n```\n````\n\n## Line Numbers\n\n````mdx\n```ts showLineNumbers\n// This will show line numbers.\n```\n````\n"
  },
  {
    "path": "content/docs/documentation/components.mdx",
    "content": "---\ntitle: Components\ndescription: Use React components in Markdown using MDX.\n---\n\nThe following components are available out of the box for use in Markdown.\n\nIf you'd like to build and add your own custom components, see the [Custom Components](#custom-components) section below.\n\n## Built-in Components\n\n### 1. Callout\n\n```mdx\n<Callout type=\" default | warning | danger \">\n\nThis is a default callout. You can embed **Markdown** inside a `callout`.\n\n</Callout>\n```\n\n<Callout>\n\nThis is a default callout. You can embed **Markdown** inside a `callout`.\n\n</Callout>\n\n<Callout type=\"warning\">\n\nThis is a warning callout. It uses the props `type=\"warning\"`.\n\n</Callout>\n\n<Callout type=\"danger\">\n\nThis is a danger callout. It uses the props `type=\"danger\"`.\n\n</Callout>\n\n### 2. Card\n\n```mdx\n<Card href=\"#\">\n\n#### Heading\n\nYou can use **markdown** inside cards.\n\n</Card>\n```\n\n<Card href=\"#\">\n\n#### Heading\n\nYou can use **markdown** inside cards.\n\n</Card>\n\nYou can also use HTML to embed cards in a grid.\n\n```mdx\n<div className=\"grid grid-cols-2 gap-4\">\n  <Card href=\"#\">\n    #### Card One \n    You can use **markdown** inside cards.\n  </Card>\n\n  <Card href=\"#\">\n    #### Card Two \n    You can also use `inline code` and code blocks.\n  </Card>\n</div>\n```\n\n<div className=\"grid grid-cols-2 gap-4\">\n  <Card href=\"#\">\n    #### Card One \n    You can use **markdown** inside cards.\n  </Card>\n\n  <Card href=\"#\">\n    #### Card Two \n    You can also use `inline code` and code blocks.\n  </Card>\n</div>\n\n---\n\n## Custom Components\n\nYou can add your own custom components using the `components` props from `useMDXComponent`.\n\n```ts title=\"components/mdx.tsx\" {2,6}\nimport { Callout } from \"@/components/callout\"\nimport { CustomComponent } from \"@/components/custom\"\n\nconst components = {\n  Callout,\n  CustomComponent,\n}\n\nexport function Mdx({ code }) {\n  const Component = useMDXComponent(code)\n\n  return (\n    <div className=\"mdx\">\n      <Component components={components} />\n    </div>\n  )\n}\n```\n\nOnce you've added your custom component, you can use it in MDX as follows:\n\n```js\n<CustomComponent propName=\"value\" />\n```\n\n---\n\n## HTML Elements\n\nYou can overwrite HTML elements using the same technique above.\n\n```ts {4}\nconst components = {\n  Callout,\n  CustomComponent,\n  hr: ({ ...props }) => <hr className=\"my-4 border-slate-200 md:my-6\" />,\n}\n```\n\nThis will overwrite the `<hr />` tag or `---` in Mardown with the HTML output above.\n\n---\n\n## Styling\n\nTailwind CSS classes can be used inside MDX for styling.\n\n```mdx\n<p className=\"text-red-600\">This text will be red.</p>\n```\n\nMake sure you have configured the path to your content in your `tailwind.config.js` file:\n\n```js title=\"tailwind.config.js\" {6}\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    \"./app/**/*.{ts,tsx}\",\n    \"./components/**/*.{ts,tsx}\",\n    \"./content/**/*.{md,mdx}\",\n  ],\n}\n```\n"
  },
  {
    "path": "content/docs/documentation/index.mdx",
    "content": "---\ntitle: Documentation\ndescription: Build your documentation site using Contentlayer and MDX.\n---\n\nTaxonomy includes a documentation site built using [Contentlayer](https://contentlayer.dev) and [MDX](http://mdxjs.com).\n\n## Features\n\nIt comes with the following features out of the box:\n\n1. Write content using MDX.\n2. Transform and validate content using Contentlayer.\n3. MDX components such as `<Callout />` and `<Card />`.\n4. Support for table of contents.\n5. Custom navigation with prev and next pager.\n6. Beautiful code blocks using `rehype-pretty-code`.\n7. Syntax highlighting using `shiki`.\n8. Built-in search (_in progress_).\n9. Dark mode (_in progress_).\n\n## How is it built\n\nClick on a section below to learn how the documentation site built.\n\n<div className=\"grid gap-4 mt-6\">\n\n<Card href=\"/docs/documentation\">\n\n### Contentlayer\n\nLearn how to use MDX with Contentlayer.\n\n</Card>\n\n<Card href=\"/docs/documentation/components\">\n\n### Components\n\nUsing React components in Mardown.\n\n</Card>\n\n<Card href=\"/docs/documentation/components\">\n\n### Code Blocks\n\nBeautiful code blocks with syntax highlighting.\n\n</Card>\n\n<Card href=\"/docs/documentation/components\">\n\n### Style Guide\n\nView a sample page with all the styles.\n\n</Card>\n\n</div>\n"
  },
  {
    "path": "content/docs/documentation/style-guide.mdx",
    "content": "---\ntitle: Style Guide\ndescription: Testing the MDX style guide with Tailwind Typography\n---\n\n<Callout>\n\n- The text below is from the [Tailwind CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it here to test the markdown styles. **Tailwind is awesome. You should use it.**\n- The CSS is from MDX sites I've built through the years. I copied this from [Nextra](https://github.com/shuding/nextra) and tweaked it a bit to fit the styles of this site.\n\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/docs/in-progress.mdx",
    "content": "---\ntitle: Not Implemented\ndescription: This page is in progress.\n---\n\n<Callout>\n\nThis site is a work in progress. If you see dummy text on a page, it means I'm still working on it. You can follow updates on Twitter [@shadcn](https://twitter.com/shadcn).\n\n</Callout>\n"
  },
  {
    "path": "content/docs/index.mdx",
    "content": "---\ntitle: Documentation\ndescription: Welcome to the Taxonomy documentation.\n---\n\nThis is the documentation for the Taxonomy site.\n\nThis is an example of a doc site built using [ContentLayer](/docs/documentation/contentlayer) and MDX.\n\n<Callout>\n\nThis site is a work in progress. If you see dummy text on a page, it means I'm still working on it. You can follow updates on Twitter [@shadcn](https://twitter.com/shadcn).\n\n</Callout>\n\n## Features\n\nSelect a feature below to learn more about it.\n\n<div className=\"grid sm:grid-cols-2 gap-4 mt-6\">\n\n<Card href=\"/docs/documentation\">\n\n### Documentation\n\nThis documentation site built using Contentlayer.\n\n</Card>\n\n<Card href=\"/docs/marketing\" disabled>\n\n### Marketing\n\nThe marketing site with landing pages.\n\n</Card>\n\n<Card href=\"/docs/app\" disabled>\n\n### App\n\nThe dashboard with auth and subscriptions.\n\n</Card>\n\n<Card href=\"/docs/blog\" disabled>\n\n### Blog\n\nThe blog built using Contentlayer and MDX.\n\n</Card>\n\n</div>\n"
  },
  {
    "path": "content/guides/build-blog-using-contentlayer-mdx.mdx",
    "content": "---\ntitle: Build a blog using ContentLayer and MDX.\ndescription: Learn how to use ContentLayer to build a blog with Next.js\ndate: 2022-11-18\n---\n\n<Callout>\n\nThis site is a work in progress. If you see dummy text on a page, it means I'm still working on it. You can follow updates on Twitter [@shadcn](https://twitter.com/shadcn).\n\n</Callout>\n\n<Callout>\n  The text below is from the [Tailwind\n  CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it\n  here to test the markdown styles. **Tailwind is awesome. You should use it.**\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/guides/using-next-auth-next-13.mdx",
    "content": "---\ntitle: Using NextAuth.js with Next.13\ndescription: How to use NextAuth.js in server components.\ndate: 2022-11-23\n---\n\n<Callout>\n\nThis site is a work in progress. If you see dummy text on a page, it means I'm still working on it. You can follow updates on Twitter [@shadcn](https://twitter.com/shadcn).\n\n</Callout>\n\n<Callout>\n  The text below is from the [Tailwind\n  CSS](https://play.tailwindcss.com/uj1vGACRJA?layout=preview) docs. I copied it\n  here to test the markdown styles. **Tailwind is awesome. You should use it.**\n</Callout>\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThe `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.\n\nIt adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread\n    with cheese to their children, with the food earning such an iconic status\n    in our culture that kids will often dress up as warm, cheesy loaf for\n    Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a\n    series of rabies cases springing up around the country.\n  </p>\n</article>\n```\n\nFor more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).\n\n---\n\n## What to expect from here on out\n\nWhat follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.\n\nIt's important to cover all of these use cases for a few reasons:\n\n1.  We want everything to look good out of the box.\n2.  Really just the first reason, that's the whole point of the plugin.\n3.  Here's a third pretend reason though a list with three items looks more realistic than a list with two items.\n\nNow we're going to try out another header style.\n\n### Typography should be easy\n\nSo that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.\n\nSomething a wise person once told me about typography is:\n\n> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.\n\nIt's probably important that images look okay here by default as well:\n\n<Image\n  src=\"/images/blog/blog-post-4.jpg\"\n  width=\"718\"\n  height=\"404\"\n  alt=\"Image\"\n/>\n\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.\n\nNow I'm going to show you an example of an unordered list to make sure that looks good, too:\n\n- So here is the first item in this list.\n- In this example we're keeping the items short.\n- Later, we'll use longer, more complex list items.\n\nAnd that's the end of this section.\n\n## What if we stack headings?\n\n### We should make sure that looks good, too.\n\nSometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.\n\n### When a heading comes after a paragraph …\n\nWhen a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.\n\n- **I often do this thing where list items have headings.**\n\n  For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.\n\n  I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.\n\n- **Since this is a list, I need at least two items.**\n\n  I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.\n\n- **It's not a bad idea to add a third item either.**\n\n  I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.\n\nAfter this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.\n\n## Code should look okay by default.\n\nI think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.\n\nHere's what a default `tailwind.config.js` file looks like at the time of writing:\n\n```js\nmodule.exports = {\n  purge: [],\n  theme: {\n    extend: {},\n  },\n  variants: {},\n  plugins: [],\n}\n```\n\nHopefully that looks good enough to you.\n\n### What about nested lists?\n\nNested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.\n\n1.  **Nested lists are rarely a good idea.**\n    - You might feel like you are being really \"organized\" or something but you are just creating a gross shape on the screen that is hard to read.\n    - Nested navigation in UIs is a bad idea too, keep things as flat as possible.\n    - Nesting tons of folders in your source code is also not helpful.\n2.  **Since we need to have more items, here's another one.**\n    - I'm not sure if we'll bother styling more than two levels deep.\n    - Two is already too much, three is guaranteed to be a bad idea.\n    - If you nest four levels deep you belong in prison.\n3.  **Two items isn't really a list, three is good though.**\n    - Again please don't nest lists if you want people to actually read your content.\n    - Nobody wants to look at this.\n    - I'm upset that we even have to bother styling this.\n\nThe most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.\n\n- **For example, here's another nested list.**\n\n  But this time with a second paragraph.\n\n  - These list items won't have `<p>` tags\n  - Because they are only one line each\n\n- **But in this second top-level list item, they will.**\n\n  This is especially annoying because of the spacing on this paragraph.\n\n  - As you can see here, because I've added a second line, this list item now has a `<p>` tag.\n\n    This is the second line I'm talking about by the way.\n\n  - Finally here's another list item so it's more like a list.\n\n- A closing list item, but with no nested list, because why not?\n\nAnd finally a sentence to close off this section.\n\n## There are other elements we need to style\n\nI almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.\n\nWe even included table styles, check it out:\n\n| Wrestler                | Origin       | Finisher           |\n| ----------------------- | ------------ | ------------------ |\n| Bret \"The Hitman\" Hart  | Calgary, AB  | Sharpshooter       |\n| Stone Cold Steve Austin | Austin, TX   | Stone Cold Stunner |\n| Randy Savage            | Sarasota, FL | Elbow Drop         |\n| Vader                   | Boulder, CO  | Vader Bomb         |\n| Razor Ramon             | Chuluota, FL | Razor's Edge       |\n\nWe also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.\n\n### Sometimes I even use `code` in headings\n\nEven though it's probably a bad idea, and historically I've had a hard time making it look good. This _\"wrap the code blocks in backticks\"_ trick works pretty well though really.\n\nAnother thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.\n\n#### We haven't used an `h4` yet\n\nBut now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.\n\nWe don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.\n\n### We still need to think about stacked headings though.\n\n#### Let's make sure we don't screw that up with `h4` elements, either.\n\nPhew, with any luck we have styled the headings above this text and they look pretty good.\n\nLet's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.\n\nWhat I've written here is probably long enough, but adding this final sentence can't hurt.\n\n## GitHub Flavored Markdown\n\nI've also added support for GitHub Flavored Mardown using `remark-gfm`.\n\nWith `remark-gfm`, we get a few extra features in our markdown. Example: autolink literals.\n\nA link like www.example.com or https://example.com would automatically be converted into an `a` tag.\n\nThis works for email links too: contact@example.com.\n"
  },
  {
    "path": "content/pages/privacy.mdx",
    "content": "---\ntitle: Privacy\ndescription: The Privacy Policy for Taxonomy App.\n---\n\nBlandit libero volutpat sed cras ornare arcu. Cursus sit amet dictum sit amet. Nunc vel risus commodo viverra maecenas accumsan. Libero id faucibus nisl tincidunt eget nullam non nisi est. Varius quam quisque id diam vel quam. Id donec ultrices tincidunt arcu non.\n\n## Consent\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu. Nibh ipsum consequat nisl vel pretium lectus quam id leo. A arcu cursus vitae congue. Amet justo donec enim diam. Vel pharetra vel turpis nunc eget lorem. Gravida quis blandit turpis cursus in. Semper auctor neque vitae tempus. Elementum facilisis leo vel fringilla est ullamcorper eget nulla. Imperdiet nulla malesuada pellentesque elit eget.\n\nFelis donec et odio pellentesque diam volutpat commodo sed.\n\nTortor consequat id porta nibh. Fames ac turpis egestas maecenas pharetra convallis posuere morbi leo. Scelerisque fermentum dui faucibus in. Tortor posuere ac ut consequat semper viverra.\n\n## Information we collect\n\nAmet justo donec enim diam. In hendrerit gravida rutrum quisque non. Hac habitasse platea dictumst quisque sagittis purus sit.\n\n## How we use your Information\n\nUt sem nulla pharetra diam sit amet nisl suscipit adipiscing. Consectetur adipiscing elit pellentesque habitant. Ut tristique et egestas quis ipsum suspendisse ultrices gravida.\n"
  },
  {
    "path": "content/pages/terms.mdx",
    "content": "---\ntitle: Terms & Conditions\ndescription: Read our terms and conditions.\n---\n\nLorem ipsumMagna fermentum iaculis eu non diam. Vitae purus faucibus ornare suspendisse sed nisi lacus sed. In nibh mauris cursus mattis molestie a iaculis at. Enim sit amet venenatis urna. Eget sit amet tellus cras adipiscing.\n\n## Legal Notices\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu. Nibh ipsum consequat nisl vel pretium lectus quam id leo. A arcu cursus vitae congue. Amet justo donec enim diam. Vel pharetra vel turpis nunc eget lorem. Gravida quis blandit turpis cursus in. Semper auctor neque vitae tempus. Elementum facilisis leo vel fringilla est ullamcorper eget nulla. Imperdiet nulla malesuada pellentesque elit eget.\n\nFelis donec et odio pellentesque diam volutpat commodo sed.\n\nTortor consequat id porta nibh. Fames ac turpis egestas maecenas pharetra convallis posuere morbi leo. Scelerisque fermentum dui faucibus in. Tortor posuere ac ut consequat semper viverra.\n\n## Warranty Disclaimer\n\nTellus in hac habitasse platea dictumst vestibulum. Faucibus in ornare quam viverra. Viverra aliquet eget sit amet tellus cras adipiscing. Erat nam at lectus urna duis convallis convallis tellus. Bibendum est ultricies integer quis auctor elit sed vulputate.\n\nNisl condimentum id venenatis a condimentum vitae. Ac auctor augue mauris augue neque gravida in fermentum. Arcu felis bibendum ut tristique. Tempor commodo ullamcorper a lacus vestibulum sed arcu non.\n\n## General\n\nMagna fermentum iaculis eu non diam. Vitae purus faucibus ornare suspendisse sed nisi lacus sed. In nibh mauris cursus mattis molestie a iaculis at. Enim sit amet venenatis urna. Eget sit amet tellus cras adipiscing.\n\nSed lectus vestibulum mattis ullamcorper velit. Id diam vel quam elementum pulvinar. In iaculis nunc sed augue lacus viverra. In hendrerit gravida rutrum quisque non tellus. Nisl purus in mollis nunc.\n\n## Disclaimer\n\nAmet justo donec enim diam. In hendrerit gravida rutrum quisque non. Hac habitasse platea dictumst quisque sagittis purus sit. Faucibus ornare suspendisse sed nisi lacus. Nulla porttitor massa id neque aliquam vestibulum. Ante in nibh mauris cursus mattis molestie a. Mi tempus imperdiet nulla malesuada.\n"
  },
  {
    "path": "contentlayer.config.js",
    "content": "import { defineDocumentType, makeSource } from \"contentlayer/source-files\"\nimport rehypeAutolinkHeadings from \"rehype-autolink-headings\"\nimport rehypePrettyCode from \"rehype-pretty-code\"\nimport rehypeSlug from \"rehype-slug\"\nimport remarkGfm from \"remark-gfm\"\n\n/** @type {import('contentlayer/source-files').ComputedFields} */\nconst computedFields = {\n  slug: {\n    type: \"string\",\n    resolve: (doc) => `/${doc._raw.flattenedPath}`,\n  },\n  slugAsParams: {\n    type: \"string\",\n    resolve: (doc) => doc._raw.flattenedPath.split(\"/\").slice(1).join(\"/\"),\n  },\n}\n\nexport const Doc = defineDocumentType(() => ({\n  name: \"Doc\",\n  filePathPattern: `docs/**/*.mdx`,\n  contentType: \"mdx\",\n  fields: {\n    title: {\n      type: \"string\",\n      required: true,\n    },\n    description: {\n      type: \"string\",\n    },\n    published: {\n      type: \"boolean\",\n      default: true,\n    },\n  },\n  computedFields,\n}))\n\nexport const Guide = defineDocumentType(() => ({\n  name: \"Guide\",\n  filePathPattern: `guides/**/*.mdx`,\n  contentType: \"mdx\",\n  fields: {\n    title: {\n      type: \"string\",\n      required: true,\n    },\n    description: {\n      type: \"string\",\n    },\n    date: {\n      type: \"date\",\n      required: true,\n    },\n    published: {\n      type: \"boolean\",\n      default: true,\n    },\n    featured: {\n      type: \"boolean\",\n      default: false,\n    },\n  },\n  computedFields,\n}))\n\nexport const Post = defineDocumentType(() => ({\n  name: \"Post\",\n  filePathPattern: `blog/**/*.mdx`,\n  contentType: \"mdx\",\n  fields: {\n    title: {\n      type: \"string\",\n      required: true,\n    },\n    description: {\n      type: \"string\",\n    },\n    date: {\n      type: \"date\",\n      required: true,\n    },\n    published: {\n      type: \"boolean\",\n      default: true,\n    },\n    image: {\n      type: \"string\",\n      required: true,\n    },\n    authors: {\n      // Reference types are not embedded.\n      // Until this is fixed, we can use a simple list.\n      // type: \"reference\",\n      // of: Author,\n      type: \"list\",\n      of: { type: \"string\" },\n      required: true,\n    },\n  },\n  computedFields,\n}))\n\nexport const Author = defineDocumentType(() => ({\n  name: \"Author\",\n  filePathPattern: `authors/**/*.mdx`,\n  contentType: \"mdx\",\n  fields: {\n    title: {\n      type: \"string\",\n      required: true,\n    },\n    description: {\n      type: \"string\",\n    },\n    avatar: {\n      type: \"string\",\n      required: true,\n    },\n    twitter: {\n      type: \"string\",\n      required: true,\n    },\n  },\n  computedFields,\n}))\n\nexport const Page = defineDocumentType(() => ({\n  name: \"Page\",\n  filePathPattern: `pages/**/*.mdx`,\n  contentType: \"mdx\",\n  fields: {\n    title: {\n      type: \"string\",\n      required: true,\n    },\n    description: {\n      type: \"string\",\n    },\n  },\n  computedFields,\n}))\n\nexport default makeSource({\n  contentDirPath: \"./content\",\n  documentTypes: [Page, Doc, Guide, Post, Author],\n  mdx: {\n    remarkPlugins: [remarkGfm],\n    rehypePlugins: [\n      rehypeSlug,\n      [\n        rehypePrettyCode,\n        {\n          theme: \"github-dark\",\n          onVisitLine(node) {\n            // Prevent lines from collapsing in `display: grid` mode, and allow empty\n            // lines to be copy/pasted\n            if (node.children.length === 0) {\n              node.children = [{ type: \"text\", value: \" \" }]\n            }\n          },\n          onVisitHighlightedLine(node) {\n            node.properties.className.push(\"line--highlighted\")\n          },\n          onVisitHighlightedWord(node) {\n            node.properties.className = [\"word--highlighted\"]\n          },\n        },\n      ],\n      [\n        rehypeAutolinkHeadings,\n        {\n          properties: {\n            className: [\"subheading-anchor\"],\n            ariaLabel: \"Link to section\",\n          },\n        },\n      ],\n    ],\n  },\n})\n"
  },
  {
    "path": "env.mjs",
    "content": "import { createEnv } from \"@t3-oss/env-nextjs\"\nimport { z } from \"zod\"\n\nexport const env = createEnv({\n  server: {\n    // This is optional because it's only used in development.\n    // See https://next-auth.js.org/deployment.\n    NEXTAUTH_URL: z.string().url().optional(),\n    NEXTAUTH_SECRET: z.string().min(1),\n    GITHUB_CLIENT_ID: z.string().min(1),\n    GITHUB_CLIENT_SECRET: z.string().min(1),\n    GITHUB_ACCESS_TOKEN: z.string().min(1),\n    DATABASE_URL: z.string().min(1),\n    SMTP_FROM: z.string().min(1),\n    POSTMARK_API_TOKEN: z.string().min(1),\n    POSTMARK_SIGN_IN_TEMPLATE: z.string().min(1),\n    POSTMARK_ACTIVATION_TEMPLATE: z.string().min(1),\n    STRIPE_API_KEY: z.string().min(1),\n    STRIPE_WEBHOOK_SECRET: z.string().min(1),\n    STRIPE_PRO_MONTHLY_PLAN_ID: z.string().min(1),\n  },\n  client: {\n    NEXT_PUBLIC_APP_URL: z.string().min(1),\n  },\n  runtimeEnv: {\n    NEXTAUTH_URL: process.env.NEXTAUTH_URL,\n    NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,\n    GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,\n    GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,\n    GITHUB_ACCESS_TOKEN: process.env.GITHUB_ACCESS_TOKEN,\n    DATABASE_URL: process.env.DATABASE_URL,\n    SMTP_FROM: process.env.SMTP_FROM,\n    POSTMARK_API_TOKEN: process.env.POSTMARK_API_TOKEN,\n    POSTMARK_SIGN_IN_TEMPLATE: process.env.POSTMARK_SIGN_IN_TEMPLATE,\n    POSTMARK_ACTIVATION_TEMPLATE: process.env.POSTMARK_ACTIVATION_TEMPLATE,\n    STRIPE_API_KEY: process.env.STRIPE_API_KEY,\n    STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,\n    STRIPE_PRO_MONTHLY_PLAN_ID: process.env.STRIPE_PRO_MONTHLY_PLAN_ID,\n    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,\n  },\n})\n"
  },
  {
    "path": "hooks/use-lock-body.ts",
    "content": "import * as React from \"react\"\n\n// @see https://usehooks.com/useLockBodyScroll.\nexport function useLockBody() {\n  React.useLayoutEffect((): (() => void) => {\n    const originalStyle: string = window.getComputedStyle(\n      document.body\n    ).overflow\n    document.body.style.overflow = \"hidden\"\n    return () => (document.body.style.overflow = originalStyle)\n  }, [])\n}\n"
  },
  {
    "path": "hooks/use-mounted.ts",
    "content": "import * as React from \"react\"\n\nexport function useMounted() {\n  const [mounted, setMounted] = React.useState(false)\n\n  React.useEffect(() => {\n    setMounted(true)\n  }, [])\n\n  return mounted\n}\n"
  },
  {
    "path": "lib/auth.ts",
    "content": "import { PrismaAdapter } from \"@next-auth/prisma-adapter\"\nimport { NextAuthOptions } from \"next-auth\"\nimport EmailProvider from \"next-auth/providers/email\"\nimport GitHubProvider from \"next-auth/providers/github\"\nimport { Client } from \"postmark\"\n\nimport { env } from \"@/env.mjs\"\nimport { siteConfig } from \"@/config/site\"\nimport { db } from \"@/lib/db\"\n\nconst postmarkClient = new Client(env.POSTMARK_API_TOKEN)\n\nexport const authOptions: NextAuthOptions = {\n  // huh any! I know.\n  // This is a temporary fix for prisma client.\n  // @see https://github.com/prisma/prisma/issues/16117\n  adapter: PrismaAdapter(db as any),\n  session: {\n    strategy: \"jwt\",\n  },\n  pages: {\n    signIn: \"/login\",\n  },\n  providers: [\n    GitHubProvider({\n      clientId: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n    }),\n    EmailProvider({\n      from: env.SMTP_FROM,\n      sendVerificationRequest: async ({ identifier, url, provider }) => {\n        const user = await db.user.findUnique({\n          where: {\n            email: identifier,\n          },\n          select: {\n            emailVerified: true,\n          },\n        })\n\n        const templateId = user?.emailVerified\n          ? env.POSTMARK_SIGN_IN_TEMPLATE\n          : env.POSTMARK_ACTIVATION_TEMPLATE\n        if (!templateId) {\n          throw new Error(\"Missing template id\")\n        }\n\n        const result = await postmarkClient.sendEmailWithTemplate({\n          TemplateId: parseInt(templateId),\n          To: identifier,\n          From: provider.from as string,\n          TemplateModel: {\n            action_url: url,\n            product_name: siteConfig.name,\n          },\n          Headers: [\n            {\n              // Set this to prevent Gmail from threading emails.\n              // See https://stackoverflow.com/questions/23434110/force-emails-not-to-be-grouped-into-conversations/25435722.\n              Name: \"X-Entity-Ref-ID\",\n              Value: new Date().getTime() + \"\",\n            },\n          ],\n        })\n\n        if (result.ErrorCode) {\n          throw new Error(result.Message)\n        }\n      },\n    }),\n  ],\n  callbacks: {\n    async session({ token, session }) {\n      if (token) {\n        session.user.id = token.id\n        session.user.name = token.name\n        session.user.email = token.email\n        session.user.image = token.picture\n      }\n\n      return session\n    },\n    async jwt({ token, user }) {\n      const dbUser = await db.user.findFirst({\n        where: {\n          email: token.email,\n        },\n      })\n\n      if (!dbUser) {\n        if (user) {\n          token.id = user?.id\n        }\n        return token\n      }\n\n      return {\n        id: dbUser.id,\n        name: dbUser.name,\n        email: dbUser.email,\n        picture: dbUser.image,\n      }\n    },\n  },\n}\n"
  },
  {
    "path": "lib/db.ts",
    "content": "import { PrismaClient } from \"@prisma/client\"\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var cachedPrisma: PrismaClient\n}\n\nlet prisma: PrismaClient\nif (process.env.NODE_ENV === \"production\") {\n  prisma = new PrismaClient()\n} else {\n  if (!global.cachedPrisma) {\n    global.cachedPrisma = new PrismaClient()\n  }\n  prisma = global.cachedPrisma\n}\n\nexport const db = prisma\n"
  },
  {
    "path": "lib/exceptions.ts",
    "content": "export class RequiresProPlanError extends Error {\n  constructor(message = \"This action requires a pro plan\") {\n    super(message)\n  }\n}\n"
  },
  {
    "path": "lib/session.ts",
    "content": "import { getServerSession } from \"next-auth/next\"\n\nimport { authOptions } from \"@/lib/auth\"\n\nexport async function getCurrentUser() {\n  const session = await getServerSession(authOptions)\n\n  return session?.user\n}\n"
  },
  {
    "path": "lib/stripe.ts",
    "content": "import Stripe from \"stripe\"\n\nimport { env } from \"@/env.mjs\"\n\nexport const stripe = new Stripe(env.STRIPE_API_KEY, {\n  apiVersion: \"2022-11-15\",\n  typescript: true,\n})\n"
  },
  {
    "path": "lib/subscription.ts",
    "content": "// @ts-nocheck\n// TODO: Fix this when we turn strict mode on.\nimport { UserSubscriptionPlan } from \"types\"\nimport { freePlan, proPlan } from \"@/config/subscriptions\"\nimport { db } from \"@/lib/db\"\n\nexport async function getUserSubscriptionPlan(\n  userId: string\n): Promise<UserSubscriptionPlan> {\n  const user = await db.user.findFirst({\n    where: {\n      id: userId,\n    },\n    select: {\n      stripeSubscriptionId: true,\n      stripeCurrentPeriodEnd: true,\n      stripeCustomerId: true,\n      stripePriceId: true,\n    },\n  })\n\n  if (!user) {\n    throw new Error(\"User not found\")\n  }\n\n  // Check if user is on a pro plan.\n  const isPro =\n    user.stripePriceId &&\n    user.stripeCurrentPeriodEnd?.getTime() + 86_400_000 > Date.now()\n\n  const plan = isPro ? proPlan : freePlan\n\n  return {\n    ...plan,\n    ...user,\n    stripeCurrentPeriodEnd: user.stripeCurrentPeriodEnd?.getTime(),\n    isPro,\n  }\n}\n"
  },
  {
    "path": "lib/toc.ts",
    "content": "// @ts-nocheck\n// TODO: Fix this when we turn strict mode on.\n\nimport { toc } from \"mdast-util-toc\"\nimport { remark } from \"remark\"\nimport { visit } from \"unist-util-visit\"\n\nconst textTypes = [\"text\", \"emphasis\", \"strong\", \"inlineCode\"]\n\nfunction flattenNode(node) {\n  const p = []\n  visit(node, (node) => {\n    if (!textTypes.includes(node.type)) return\n    p.push(node.value)\n  })\n  return p.join(``)\n}\n\ninterface Item {\n  title: string\n  url: string\n  items?: Item[]\n}\n\ninterface Items {\n  items?: Item[]\n}\n\nfunction getItems(node, current): Items {\n  if (!node) {\n    return {}\n  }\n\n  if (node.type === \"paragraph\") {\n    visit(node, (item) => {\n      if (item.type === \"link\") {\n        current.url = item.url\n        current.title = flattenNode(node)\n      }\n\n      if (item.type === \"text\") {\n        current.title = flattenNode(node)\n      }\n    })\n\n    return current\n  }\n\n  if (node.type === \"list\") {\n    current.items = node.children.map((i) => getItems(i, {}))\n\n    return current\n  } else if (node.type === \"listItem\") {\n    const heading = getItems(node.children[0], {})\n\n    if (node.children.length > 1) {\n      getItems(node.children[1], heading)\n    }\n\n    return heading\n  }\n\n  return {}\n}\n\nconst getToc = () => (node, file) => {\n  const table = toc(node)\n  file.data = getItems(table.map, {})\n}\n\nexport type TableOfContents = Items\n\nexport async function getTableOfContents(\n  content: string\n): Promise<TableOfContents> {\n  const result = await remark().use(getToc).process(content)\n\n  return result.data\n}\n"
  },
  {
    "path": "lib/utils.ts",
    "content": "import { ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nimport { env } from \"@/env.mjs\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n\nexport function formatDate(input: string | number): string {\n  const date = new Date(input)\n  return date.toLocaleDateString(\"en-US\", {\n    month: \"long\",\n    day: \"numeric\",\n    year: \"numeric\",\n  })\n}\n\nexport function absoluteUrl(path: string) {\n  return `${env.NEXT_PUBLIC_APP_URL}${path}`\n}\n"
  },
  {
    "path": "lib/validations/auth.ts",
    "content": "import * as z from \"zod\"\n\nexport const userAuthSchema = z.object({\n  email: z.string().email(),\n})\n"
  },
  {
    "path": "lib/validations/og.ts",
    "content": "import * as z from \"zod\"\n\nexport const ogImageSchema = z.object({\n  heading: z.string(),\n  type: z.string(),\n  mode: z.enum([\"light\", \"dark\"]).default(\"dark\"),\n})\n"
  },
  {
    "path": "lib/validations/post.ts",
    "content": "import * as z from \"zod\"\n\nexport const postPatchSchema = z.object({\n  title: z.string().min(3).max(128).optional(),\n\n  // TODO: Type this properly from editorjs block types?\n  content: z.any().optional(),\n})\n"
  },
  {
    "path": "lib/validations/user.ts",
    "content": "import * as z from \"zod\"\n\nexport const userNameSchema = z.object({\n  name: z.string().min(3).max(32),\n})\n"
  },
  {
    "path": "middleware.ts",
    "content": "import { getToken } from \"next-auth/jwt\"\nimport { withAuth } from \"next-auth/middleware\"\nimport { NextResponse } from \"next/server\"\n\nexport default withAuth(\n  async function middleware(req) {\n    const token = await getToken({ req })\n    const isAuth = !!token\n    const isAuthPage =\n      req.nextUrl.pathname.startsWith(\"/login\") ||\n      req.nextUrl.pathname.startsWith(\"/register\")\n\n    if (isAuthPage) {\n      if (isAuth) {\n        return NextResponse.redirect(new URL(\"/dashboard\", req.url))\n      }\n\n      return null\n    }\n\n    if (!isAuth) {\n      let from = req.nextUrl.pathname;\n      if (req.nextUrl.search) {\n        from += req.nextUrl.search;\n      }\n\n      return NextResponse.redirect(\n        new URL(`/login?from=${encodeURIComponent(from)}`, req.url)\n      );\n    }\n  },\n  {\n    callbacks: {\n      async authorized() {\n        // This is a work-around for handling redirect on auth pages.\n        // We return true here so that the middleware function above\n        // is always called.\n        return true\n      },\n    },\n  }\n)\n\nexport const config = {\n  matcher: [\"/dashboard/:path*\", \"/editor/:path*\", \"/login\", \"/register\"],\n}\n"
  },
  {
    "path": "next.config.mjs",
    "content": "import { withContentlayer } from \"next-contentlayer\"\n\nimport \"./env.mjs\"\n\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  reactStrictMode: true,\n  images: {\n    domains: [\"avatars.githubusercontent.com\"],\n  },\n  experimental: {\n    appDir: true,\n    serverComponentsExternalPackages: [\"@prisma/client\"],\n  },\n}\n\nexport default withContentlayer(nextConfig)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"taxonomy\",\n  \"version\": \"0.2.0\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"shadcn\",\n    \"url\": \"https://twitter.com/shadcn\"\n  },\n  \"scripts\": {\n    \"dev\": \"concurrently \\\"contentlayer dev\\\" \\\"next dev\\\"\",\n    \"build\": \"contentlayer build && next build\",\n    \"turbo\": \"next dev --turbo\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\",\n    \"preview\": \"next build && next start\",\n    \"postinstall\": \"prisma generate\"\n  },\n  \"dependencies\": {\n    \"@editorjs/code\": \"^2.8.0\",\n    \"@editorjs/editorjs\": \"^2.26.5\",\n    \"@editorjs/embed\": \"^2.5.3\",\n    \"@editorjs/header\": \"^2.7.0\",\n    \"@editorjs/inline-code\": \"^1.4.0\",\n    \"@editorjs/link\": \"^2.5.0\",\n    \"@editorjs/list\": \"^1.8.0\",\n    \"@editorjs/paragraph\": \"^2.9.0\",\n    \"@editorjs/table\": \"^2.2.1\",\n    \"@hookform/resolvers\": \"^3.1.0\",\n    \"@next-auth/prisma-adapter\": \"^1.0.6\",\n    \"@prisma/client\": \"^4.13.0\",\n    \"@radix-ui/react-accessible-icon\": \"^1.0.2\",\n    \"@radix-ui/react-accordion\": \"^1.1.1\",\n    \"@radix-ui/react-alert-dialog\": \"^1.0.3\",\n    \"@radix-ui/react-aspect-ratio\": \"^1.0.2\",\n    \"@radix-ui/react-avatar\": \"^1.0.2\",\n    \"@radix-ui/react-checkbox\": \"^1.0.3\",\n    \"@radix-ui/react-collapsible\": \"^1.0.2\",\n    \"@radix-ui/react-context-menu\": \"^2.1.3\",\n    \"@radix-ui/react-dialog\": \"^1.0.3\",\n    \"@radix-ui/react-dropdown-menu\": \"^2.0.4\",\n    \"@radix-ui/react-hover-card\": \"^1.0.5\",\n    \"@radix-ui/react-label\": \"^2.0.1\",\n    \"@radix-ui/react-menubar\": \"^1.0.2\",\n    \"@radix-ui/react-navigation-menu\": \"^1.1.2\",\n    \"@radix-ui/react-popover\": \"^1.0.5\",\n    \"@radix-ui/react-progress\": \"^1.0.2\",\n    \"@radix-ui/react-radio-group\": \"^1.1.2\",\n    \"@radix-ui/react-scroll-area\": \"^1.0.3\",\n    \"@radix-ui/react-select\": \"^1.2.1\",\n    \"@radix-ui/react-separator\": \"^1.0.2\",\n    \"@radix-ui/react-slider\": \"^1.1.1\",\n    \"@radix-ui/react-slot\": \"^1.0.1\",\n    \"@radix-ui/react-switch\": \"^1.0.2\",\n    \"@radix-ui/react-tabs\": \"^1.0.3\",\n    \"@radix-ui/react-toast\": \"^1.1.3\",\n    \"@radix-ui/react-toggle\": \"^1.0.2\",\n    \"@radix-ui/react-toggle-group\": \"^1.0.3\",\n    \"@radix-ui/react-tooltip\": \"^1.0.5\",\n    \"@t3-oss/env-nextjs\": \"^0.2.2\",\n    \"@typescript-eslint/parser\": \"^5.59.0\",\n    \"@vercel/analytics\": \"^1.0.0\",\n    \"@vercel/og\": \"^0.0.21\",\n    \"class-variance-authority\": \"^0.4.0\",\n    \"clsx\": \"^1.2.1\",\n    \"cmdk\": \"^0.1.22\",\n    \"concurrently\": \"^8.0.1\",\n    \"contentlayer\": \"^0.3.1\",\n    \"date-fns\": \"^2.29.3\",\n    \"lucide-react\": \"^0.92.0\",\n    \"next\": \"13.3.2-canary.13\",\n    \"next-auth\": \"4.22.1\",\n    \"next-contentlayer\": \"^0.3.1\",\n    \"next-themes\": \"^0.2.1\",\n    \"nodemailer\": \"^6.9.1\",\n    \"postmark\": \"^3.0.15\",\n    \"prop-types\": \"^15.8.1\",\n    \"react\": \"^18.2.0\",\n    \"react-day-picker\": \"^8.7.1\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-editor-js\": \"^2.1.0\",\n    \"react-hook-form\": \"^7.43.9\",\n    \"react-textarea-autosize\": \"^8.4.1\",\n    \"sharp\": \"^0.31.3\",\n    \"shiki\": \"^0.11.1\",\n    \"stripe\": \"^11.18.0\",\n    \"tailwind-merge\": \"^1.12.0\",\n    \"tailwindcss-animate\": \"^1.0.5\",\n    \"zod\": \"^3.21.4\"\n  },\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"^17.6.1\",\n    \"@commitlint/config-conventional\": \"^17.6.1\",\n    \"@ianvs/prettier-plugin-sort-imports\": \"^3.7.2\",\n    \"@tailwindcss/line-clamp\": \"^0.4.4\",\n    \"@tailwindcss/typography\": \"^0.5.9\",\n    \"@types/node\": \"^18.16.0\",\n    \"@types/react\": \"18.0.15\",\n    \"@types/react-dom\": \"18.0.6\",\n    \"autoprefixer\": \"^10.4.14\",\n    \"eslint\": \"^8.39.0\",\n    \"eslint-config-next\": \"13.0.0\",\n    \"eslint-config-prettier\": \"^8.8.0\",\n    \"eslint-plugin-react\": \"^7.32.2\",\n    \"eslint-plugin-tailwindcss\": \"^3.11.0\",\n    \"husky\": \"^8.0.3\",\n    \"mdast-util-toc\": \"^6.1.1\",\n    \"postcss\": \"^8.4.23\",\n    \"prettier\": \"^2.8.8\",\n    \"prettier-plugin-tailwindcss\": \"^0.1.13\",\n    \"pretty-quick\": \"^3.1.3\",\n    \"prisma\": \"^4.13.0\",\n    \"rehype\": \"^12.0.1\",\n    \"rehype-autolink-headings\": \"^6.1.1\",\n    \"rehype-pretty-code\": \"^0.9.5\",\n    \"rehype-slug\": \"^5.1.0\",\n    \"remark\": \"^14.0.2\",\n    \"remark-gfm\": \"^3.0.1\",\n    \"tailwindcss\": \"^3.3.1\",\n    \"typescript\": \"4.7.4\",\n    \"unist-util-visit\": \"^4.1.2\"\n  }\n}\n"
  },
  {
    "path": "pages/api/auth/[...nextauth].ts",
    "content": "import NextAuth from \"next-auth\"\n\nimport { authOptions } from \"@/lib/auth\"\n\n// @see ./lib/auth\nexport default NextAuth(authOptions)\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "prettier.config.js",
    "content": "/** @type {import('prettier').Config} */\nmodule.exports = {\n  endOfLine: \"lf\",\n  semi: false,\n  singleQuote: false,\n  tabWidth: 2,\n  trailingComma: \"es5\",\n  importOrder: [\n    \"^(react/(.*)$)|^(react$)\",\n    \"^(next/(.*)$)|^(next$)\",\n    \"<THIRD_PARTY_MODULES>\",\n    \"\",\n    \"^types$\",\n    \"^@/env(.*)$\",\n    \"^@/types/(.*)$\",\n    \"^@/config/(.*)$\",\n    \"^@/lib/(.*)$\",\n    \"^@/hooks/(.*)$\",\n    \"^@/components/ui/(.*)$\",\n    \"^@/components/(.*)$\",\n    \"^@/styles/(.*)$\",\n    \"^@/app/(.*)$\",\n    \"\",\n    \"^[./]\",\n  ],\n  importOrderSeparation: false,\n  importOrderSortSpecifiers: true,\n  importOrderBuiltinModulesToTop: true,\n  importOrderParserPlugins: [\"typescript\", \"jsx\", \"decorators-legacy\"],\n  importOrderMergeDuplicateImports: true,\n  importOrderCombineTypeAndValueImports: true,\n  plugins: [\"@ianvs/prettier-plugin-sort-imports\"],\n}\n"
  },
  {
    "path": "prisma/migrations/20221021182747_init/migration.sql",
    "content": "-- CreateTable\nCREATE TABLE `accounts` (\n    `id` VARCHAR(191) NOT NULL,\n    `userId` VARCHAR(191) NOT NULL,\n    `type` VARCHAR(191) NOT NULL,\n    `provider` VARCHAR(191) NOT NULL,\n    `providerAccountId` VARCHAR(191) NOT NULL,\n    `refresh_token` TEXT NULL,\n    `access_token` TEXT NULL,\n    `expires_at` INTEGER NULL,\n    `token_type` VARCHAR(191) NULL,\n    `scope` VARCHAR(191) NULL,\n    `id_token` TEXT NULL,\n    `session_state` VARCHAR(191) NULL,\n    `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n    `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n\n    UNIQUE INDEX `accounts_provider_providerAccountId_key`(`provider`, `providerAccountId`),\n    PRIMARY KEY (`id`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- CreateTable\nCREATE TABLE `sessions` (\n    `id` VARCHAR(191) NOT NULL,\n    `sessionToken` VARCHAR(191) NOT NULL,\n    `userId` VARCHAR(191) NOT NULL,\n    `expires` DATETIME(3) NOT NULL,\n\n    UNIQUE INDEX `sessions_sessionToken_key`(`sessionToken`),\n    PRIMARY KEY (`id`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- CreateTable\nCREATE TABLE `users` (\n    `id` VARCHAR(191) NOT NULL,\n    `name` VARCHAR(191) NULL,\n    `email` VARCHAR(191) NULL,\n    `emailVerified` DATETIME(3) NULL,\n    `image` VARCHAR(191) NULL,\n    `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n    `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n\n    UNIQUE INDEX `users_email_key`(`email`),\n    PRIMARY KEY (`id`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- CreateTable\nCREATE TABLE `verification_tokens` (\n    `identifier` VARCHAR(191) NOT NULL,\n    `token` VARCHAR(191) NOT NULL,\n    `expires` DATETIME(3) NOT NULL,\n\n    UNIQUE INDEX `verification_tokens_token_key`(`token`),\n    UNIQUE INDEX `verification_tokens_identifier_token_key`(`identifier`, `token`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- CreateTable\nCREATE TABLE `posts` (\n    `id` VARCHAR(191) NOT NULL,\n    `title` VARCHAR(191) NOT NULL,\n    `content` JSON NULL,\n    `published` BOOLEAN NOT NULL DEFAULT false,\n    `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n    `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n    `authorId` VARCHAR(191) NOT NULL,\n\n    PRIMARY KEY (`id`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- AddForeignKey\nALTER TABLE `accounts` ADD CONSTRAINT `accounts_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE `sessions` ADD CONSTRAINT `sessions_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE `posts` ADD CONSTRAINT `posts_authorId_fkey` FOREIGN KEY (`authorId`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;\n"
  },
  {
    "path": "prisma/migrations/20221118173244_add_stripe_columns/migration.sql",
    "content": "/*\n  Warnings:\n\n  - A unique constraint covering the columns `[stripe_customer_id]` on the table `users` will be added. If there are existing duplicate values, this will fail.\n  - A unique constraint covering the columns `[stripe_subscription_id]` on the table `users` will be added. If there are existing duplicate values, this will fail.\n\n*/\n-- DropForeignKey\nALTER TABLE `accounts` DROP FOREIGN KEY `accounts_userId_fkey`;\n\n-- DropForeignKey\nALTER TABLE `posts` DROP FOREIGN KEY `posts_authorId_fkey`;\n\n-- DropForeignKey\nALTER TABLE `sessions` DROP FOREIGN KEY `sessions_userId_fkey`;\n\n-- AlterTable\nALTER TABLE `users` ADD COLUMN `stripe_current_period_end` DATETIME(3) NULL,\n    ADD COLUMN `stripe_customer_id` VARCHAR(191) NULL,\n    ADD COLUMN `stripe_price_id` VARCHAR(191) NULL,\n    ADD COLUMN `stripe_subscription_id` VARCHAR(191) NULL;\n\n-- CreateIndex\nCREATE UNIQUE INDEX `users_stripe_customer_id_key` ON `users`(`stripe_customer_id`);\n\n-- CreateIndex\nCREATE UNIQUE INDEX `users_stripe_subscription_id_key` ON `users`(`stripe_subscription_id`);\n"
  },
  {
    "path": "prisma/migrations/migration_lock.toml",
    "content": "# Please do not edit this file manually\n# It should be added in your version-control system (i.e. Git)\nprovider = \"mysql\""
  },
  {
    "path": "prisma/schema.prisma",
    "content": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"mysql\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel Account {\n  id                String   @id @default(cuid())\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?  @db.Text\n  access_token      String?  @db.Text\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?  @db.Text\n  session_state     String?\n  createdAt         DateTime @default(now()) @map(name: \"created_at\")\n  updatedAt         DateTime @default(now()) @map(name: \"updated_at\")\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([provider, providerAccountId])\n  @@map(name: \"accounts\")\n}\n\nmodel Session {\n  id           String   @id @default(cuid())\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@map(name: \"sessions\")\n}\n\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String?   @unique\n  emailVerified DateTime?\n  image         String?\n  createdAt     DateTime  @default(now()) @map(name: \"created_at\")\n  updatedAt     DateTime  @default(now()) @map(name: \"updated_at\")\n\n  accounts Account[]\n  sessions Session[]\n  Post     Post[]\n\n  stripeCustomerId       String?   @unique @map(name: \"stripe_customer_id\")\n  stripeSubscriptionId   String?   @unique @map(name: \"stripe_subscription_id\")\n  stripePriceId          String?   @map(name: \"stripe_price_id\")\n  stripeCurrentPeriodEnd DateTime? @map(name: \"stripe_current_period_end\")\n\n  @@map(name: \"users\")\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String   @unique\n  expires    DateTime\n\n  @@unique([identifier, token])\n  @@map(name: \"verification_tokens\")\n}\n\nmodel Post {\n  id        String   @id @default(cuid())\n  title     String\n  content   Json?\n  published Boolean  @default(false)\n  createdAt DateTime @default(now()) @map(name: \"created_at\")\n  updatedAt DateTime @default(now()) @map(name: \"updated_at\")\n  authorId  String\n\n  author User @relation(fields: [authorId], references: [id])\n\n  @@map(name: \"posts\")\n}\n"
  },
  {
    "path": "public/site.webmanifest",
    "content": "{\n  \"name\": \"Taxonomy\",\n  \"short_name\": \"Taxonomy\",\n  \"icons\": [\n    {\n      \"src\": \"/android-chrome-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"/android-chrome-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\"\n    }\n  ],\n  \"theme_color\": \"#ffffff\",\n  \"background_color\": \"#ffffff\",\n  \"display\": \"standalone\"\n}\n"
  },
  {
    "path": "styles/editor.css",
    "content": ".dark .ce-block--selected .ce-block__content,\n.dark .ce-inline-toolbar,\n.dark .codex-editor--narrow .ce-toolbox,\n.dark .ce-conversion-toolbar,\n.dark .ce-settings,\n.dark .ce-settings__button,\n.dark .ce-toolbar__settings-btn,\n.dark .cdx-button,\n.dark .ce-popover,\n.dark .ce-toolbar__plus:hover {\n  background: theme('colors.popover.DEFAULT');\n  color: inherit;\n\tborder-color: theme('colors.border');\n}\n\n.dark .ce-inline-tool,\n.dark .ce-conversion-toolbar__label,\n.dark .ce-toolbox__button,\n.dark .cdx-settings-button,\n.dark .ce-toolbar__plus {\n  color: inherit;\n}\n\n.dark .ce-popover__item-icon,\n.dark .ce-conversion-tool__icon {\n\tbackground-color: theme('colors.muted.DEFAULT');\n\tbox-shadow: none;\n}\n\n.dark .cdx-search-field {\n\tborder-color: theme('colors.border');\n\tbackground: theme('colors.input');\n\tcolor: inherit;\n}\n\n.dark ::selection {\n  background: theme('colors.accent.DEFAULT');\n}\n\n.dark .cdx-settings-button:hover,\n.dark .ce-settings__button:hover,\n.dark .ce-toolbox__button--active,\n.dark .ce-toolbox__button:hover,\n.dark .cdx-button:hover,\n.dark .ce-inline-toolbar__dropdown:hover,\n.dark .ce-inline-tool:hover,\n.dark .ce-popover__item:hover,\n.dark .ce-conversion-tool:hover,\n.dark .ce-toolbar__settings-btn:hover {\n  background-color:theme('colors.accent.DEFAULT');\n  color: theme('colors.accent.foreground');;\n}\n\n.dark .cdx-notify--error {\n  background: theme('colors.destructive.DEFAULT') !important;\n}\n\n.dark .cdx-notify__cross::after,\n.dark .cdx-notify__cross::before {\n  background: white;\n}"
  },
  {
    "path": "styles/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 47.4% 11.2%;\n\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 47.4% 11.2%;\n\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 47.4% 11.2%;\n\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n\n    --destructive: 0 100% 50%;\n    --destructive-foreground: 210 40% 98%;\n\n    --ring: 215 20.2% 65.1%;\n\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 224 71% 4%;\n    --foreground: 213 31% 91%;\n\n    --muted: 223 47% 11%;\n    --muted-foreground: 215.4 16.3% 56.9%;\n\n    --accent: 216 34% 17%;\n    --accent-foreground: 210 40% 98%;\n\n    --popover: 224 71% 4%;\n    --popover-foreground: 215 20.2% 65.1%;\n\n    --border: 216 34% 17%;\n    --input: 216 34% 17%;\n\n    --card: 224 71% 4%;\n    --card-foreground: 213 31% 91%;\n\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 1.2%;\n\n    --secondary: 222.2 47.4% 11.2%;\n    --secondary-foreground: 210 40% 98%;\n\n    --destructive: 0 63% 31%;\n    --destructive-foreground: 210 40% 98%;\n\n    --ring: 216 34% 17%;\n\n    --radius: 0.5rem;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n    font-feature-settings: \"rlig\" 1, \"calt\" 1;\n  }\n}\n"
  },
  {
    "path": "styles/mdx.css",
    "content": "[data-rehype-pretty-code-fragment] code {\n  @apply grid min-w-full break-words rounded-none border-0 bg-transparent p-0 text-sm text-black;\n  counter-reset: line;\n  box-decoration-break: clone;\n}\n[data-rehype-pretty-code-fragment] .line {\n  @apply px-4 py-1;\n}\n[data-rehype-pretty-code-fragment] [data-line-numbers] > .line::before {\n  counter-increment: line;\n  content: counter(line);\n  display: inline-block;\n  width: 1rem;\n  margin-right: 1rem;\n  text-align: right;\n  color: gray;\n}\n[data-rehype-pretty-code-fragment] .line--highlighted {\n  @apply bg-slate-300 bg-opacity-10;\n}\n[data-rehype-pretty-code-fragment] .line-highlighted span {\n  @apply relative;\n}\n[data-rehype-pretty-code-fragment] .word--highlighted {\n  @apply rounded-md bg-slate-300 bg-opacity-10 p-1;\n}\n[data-rehype-pretty-code-title] {\n  @apply mt-4 py-2 px-4 text-sm font-medium;\n}\n[data-rehype-pretty-code-title] + pre {\n  @apply mt-0;\n}\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "const { fontFamily } = require(\"tailwindcss/defaultTheme\")\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    \"./app/**/*.{ts,tsx}\",\n    \"./components/**/*.{ts,tsx}\",\n    \"./ui/**/*.{ts,tsx}\",\n    \"./content/**/*.{md,mdx}\",\n  ],\n  darkMode: [\"class\"],\n  theme: {\n    container: {\n      center: true,\n      padding: \"2rem\",\n      screens: {\n        \"2xl\": \"1400px\",\n      },\n    },\n    extend: {\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n      },\n      borderRadius: {\n        lg: `var(--radius)`,\n        md: `calc(var(--radius) - 2px)`,\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n      fontFamily: {\n        sans: [\"var(--font-sans)\", ...fontFamily.sans],\n        heading: [\"var(--font-heading)\", ...fontFamily.sans],\n      },\n      keyframes: {\n        \"accordion-down\": {\n          from: { height: 0 },\n          to: { height: \"var(--radix-accordion-content-height)\" },\n        },\n        \"accordion-up\": {\n          from: { height: \"var(--radix-accordion-content-height)\" },\n          to: { height: 0 },\n        },\n      },\n      animation: {\n        \"accordion-down\": \"accordion-down 0.2s ease-out\",\n        \"accordion-up\": \"accordion-up 0.2s ease-out\",\n      },\n    },\n  },\n  plugins: [require(\"tailwindcss-animate\"), require(\"@tailwindcss/typography\")],\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./*\"],\n      \"contentlayer/generated\": [\"./.contentlayer/generated\"]\n    },\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"strictNullChecks\": true\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \".next/types/**/*.ts\",\n    \".contentlayer/generated\"\n  ],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "types/index.d.ts",
    "content": "import { User } from \"@prisma/client\"\nimport type { Icon } from \"lucide-react\"\n\nimport { Icons } from \"@/components/icons\"\n\nexport type NavItem = {\n  title: string\n  href: string\n  disabled?: boolean\n}\n\nexport type MainNavItem = NavItem\n\nexport type SidebarNavItem = {\n  title: string\n  disabled?: boolean\n  external?: boolean\n  icon?: keyof typeof Icons\n} & (\n  | {\n      href: string\n      items?: never\n    }\n  | {\n      href?: string\n      items: NavLink[]\n    }\n)\n\nexport type SiteConfig = {\n  name: string\n  description: string\n  url: string\n  ogImage: string\n  links: {\n    twitter: string\n    github: string\n  }\n}\n\nexport type DocsConfig = {\n  mainNav: MainNavItem[]\n  sidebarNav: SidebarNavItem[]\n}\n\nexport type MarketingConfig = {\n  mainNav: MainNavItem[]\n}\n\nexport type DashboardConfig = {\n  mainNav: MainNavItem[]\n  sidebarNav: SidebarNavItem[]\n}\n\nexport type SubscriptionPlan = {\n  name: string\n  description: string\n  stripePriceId: string\n}\n\nexport type UserSubscriptionPlan = SubscriptionPlan &\n  Pick<User, \"stripeCustomerId\" | \"stripeSubscriptionId\"> & {\n    stripeCurrentPeriodEnd: number\n    isPro: boolean\n  }\n"
  },
  {
    "path": "types/next-auth.d.ts",
    "content": "import { User } from \"next-auth\"\nimport { JWT } from \"next-auth/jwt\"\n\ntype UserId = string\n\ndeclare module \"next-auth/jwt\" {\n  interface JWT {\n    id: UserId\n  }\n}\n\ndeclare module \"next-auth\" {\n  interface Session {\n    user: User & {\n      id: UserId\n    }\n  }\n}\n"
  }
]