[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": ".github/workflows/npm-gulp.yml",
    "content": "name: NodeJS with Gulp\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [14.x, 16.x, 18.x]\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v3\n      with:\n        node-version: ${{ matrix.node-version }}\n\n    - name: Build\n      run: |\n        npm install\n        gulp\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\n# local env files\n.env*.local\n.env\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Ayush Rathore\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": "# AI-SaaS - AI-Powered Software-as-a-Service Application\n\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![License](https://img.shields.io/badge/React.js-v18.2.0-blue.svg)](https://opensource.org/licenses/MIT)\n[![Next.js](https://img.shields.io/badge/Next.js-v13.4.12-blueviolet.svg)](https://nextjs.org/)\n[![OpenAI](https://img.shields.io/badge/OpenAI-API-yellow.svg)](https://openai.com/)\n[![Replicate](https://img.shields.io/badge/Replicate-v0.12.3-orange.svg)](https://replicate.ai/)\n[![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-v3.3.3-blue.svg)](https://tailwindcss.com/)\n[![Prisma](https://img.shields.io/badge/Prisma-v5.0.0-lightgrey.svg)](https://prisma.io/)\n[![Stripe](https://img.shields.io/badge/Stripe-API%20v12.16.0-green.svg)](https://stripe.com/)\n\nAI-SaaS is an advanced and adaptable Software-as-a-Service (SaaS) application that harnesses the capabilities of cutting-edge technologies, including Next.js, OpenAI, Replicate, Tailwind CSS, Prisma, and Stripe. The primary goal of this application is to empower users by offering AI-powered services that facilitate easy access and utilization of artificial intelligence in their projects and workflows.\n\n## Features\n\n- **AI Services**: AI-SaaS provides an extensive array of AI services, including conversation, code generation, image generation, music generation, and video generation. These services are accessible through an intuitive and user-friendly interface.\n\n- **Next.js**: AI-SaaS is built on the Next.js framework, offering server-side rendering, routing, and other essential features out of the box. This ensures superior performance and search engine optimization (SEO) for the application.\n\n- **OpenAI Integration**: The application seamlessly integrates with OpenAI's powerful AI models and APIs, enabling users to leverage state-of-the-art AI capabilities. From generating human-like text to answering questions, AI-SaaS harnesses the full potential of OpenAI.\n\n- **Replicate**: AI-SaaS employs Replicate to enhance model reproducibility and facilitate seamless experimentation with various AI models. This ensures the AI models used in the application are robust and reliable.\n\n- **Tailwind CSS**: The UI of AI-SaaS is meticulously styled using Tailwind CSS, a utility-first CSS framework. This enables easy customization and consistent design throughout the application.\n\n- **Prisma**: The application utilizes Prisma as its ORM (Object-Relational Mapping) tool, simplifying database access and management. This enhances the efficiency of handling user data and preferences.\n\n- **Stripe Integration**: AI-SaaS seamlessly incorporates Stripe for secure and efficient payment processing. Users can subscribe to premium plans and access additional AI services based on their subscription level.\n\n## Screenshots\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 33 59 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/017f21c8-a8d2-4b70-b21b-60c1d03a28e0\">\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 40 43 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/a232dd89-9a6b-4f6d-bc6b-99ec9281fc78\">\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 41 18 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/a8f04ec9-2a3b-407a-8f18-3c948d1fa592\">\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 41 53 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/028ab744-870c-42d5-aa5a-78fa19a6d334\">\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 42 23 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/46e095c6-b369-4eb2-9e1e-e219e1703565\">\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 42 38 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/18f5b2d2-25b0-4b41-ad5b-1c47301a3196\">\n<img width=\"1470\" alt=\"Screenshot 2023-07-30 at 11 42 50 AM\" src=\"https://github.com/ayusshrathore/ai-saas/assets/61450246/1292a4f1-e101-4837-90bd-6018f8f61288\">\n\n## Getting Started\n\nTo run AI-SaaS locally, follow these steps:\n\n1. **Clone the repository**:\n\n```bash\ngit clone https://github.com/ayusshrathore/ai-saas.git\ncd ai-saas\n```\n\n2. **Install dependencies**:\n\n```bash\nnpm install\n# or\nyarn install\n```\n\n3. **Configure environment variables**:\n\nTo ensure proper functionality, set up environment variables for API keys and other sensitive information. Create a `.env` file in the root directory and populate it with the necessary variables. For reference, consult the `.env.example` file for the required variables.\n\n4. **Run the application**:\n\n```bash\nnpm run dev\n# or\nyarn dev\n```\n\nThe application should now be running locally at `http://localhost:3000`.\n\n## Deployment\n\nAI-SaaS can be deployed to various hosting platforms that support Next.js applications. Before deployment, make sure you have configured the necessary environment variables for production.\n\n## Contributions\n\nContributions to AI-SaaS are highly appreciated! If you encounter any bugs or have suggestions for new features, please feel free to open an issue or submit a pull request.\n\nWhen contributing, adhere to the existing code style and include comprehensive test cases for new features.\n\n## License\n\nAI-SaaS is released under the [MIT License](https://opensource.org/licenses/MIT).\n\n## Acknowledgments\n\nAI-SaaS is built with the invaluable support and integration of several open-source projects and technologies. I extend my gratitude to the developers and maintainers of Next.js, OpenAI, Replicate, Tailwind CSS, Prisma, and Stripe for their significant contributions to the development community.\n[![Netlify Status](https://api.netlify.com/api/v1/badges/6da7f929-c69e-4c0a-9fd6-596a41129274/deploy-status)](https://app.netlify.com/sites/superlative-malabi-796b55/deploys)\n"
  },
  {
    "path": "app/(auth)/(routes)/sign-in/[[...sign-in]]/page.tsx",
    "content": "import { SignIn } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return <SignIn />;\n}\n"
  },
  {
    "path": "app/(auth)/(routes)/sign-up/[[...sign-up]]/page.tsx",
    "content": "import { SignUp } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return <SignUp />;\n}\n"
  },
  {
    "path": "app/(auth)/layout.tsx",
    "content": "import React from \"react\";\n\nconst AuthLayout = ({ children }: { children: React.ReactNode }) => {\n  return <div className=\"flex items-center justify-center h-full\">{children}</div>;\n};\n\nexport default AuthLayout;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/code/constants.tsx",
    "content": "import * as z from \"zod\";\n\nexport const formSchema = z.object({\n  prompt: z.string().min(1, {\n    message: \"Prompt is required.\",\n  }),\n});\n"
  },
  {
    "path": "app/(dashboard)/(routes)/code/page.tsx",
    "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport axios from \"axios\";\nimport { Code } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { ChatCompletionRequestMessage } from \"openai\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport ReactMarkdown from \"react-markdown\";\nimport * as z from \"zod\";\n\nimport { BotAvatar } from \"@/components/bot-avatar\";\nimport { Empty } from \"@/components/empty\";\nimport { Heading } from \"@/components/heading\";\nimport { Loader } from \"@/components/loader\";\nimport { Button } from \"@/components/ui/button\";\nimport { Form, FormControl, FormField, FormItem } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { UserAvatar } from \"@/components/user-avatar\";\nimport { cn } from \"@/lib/utils\";\n\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport { toast } from \"react-hot-toast\";\nimport { formSchema } from \"./constants\";\n\nconst CodePage = () => {\n  const router = useRouter();\n  const proModal = useProModal();\n  const [messages, setMessages] = useState<ChatCompletionRequestMessage[]>([]);\n\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      prompt: \"\",\n    },\n  });\n\n  const isLoading = form.formState.isSubmitting;\n\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    console.log(values);\n    try {\n      const userMessage: ChatCompletionRequestMessage = {\n        role: \"user\",\n        content: values.prompt,\n      };\n      const newMessages = [...messages, userMessage];\n\n      const response = await axios.post(\"/api/code\", {\n        messages: newMessages,\n      });\n\n      setMessages((current) => [...current, userMessage, response.data]);\n      form.reset();\n    } catch (error: any) {\n      console.log(error);\n      if (error?.response?.status === 403) {\n        proModal.onOpen();\n      } else {\n        toast.error(\"Something went wrong.\");\n      }\n    } finally {\n      router.refresh();\n    }\n  };\n\n  return (\n    <div>\n      <Heading\n        title=\"Code Generation\"\n        description=\"Our most advanced AI Code Generation model.\"\n        icon={Code}\n        iconColor=\"text-green-700\"\n        bgColor=\"bg-green-700/10\"\n      />\n      <div className=\"px-4 lg:px-8\">\n        <div>\n          <Form {...form}>\n            <form\n              onSubmit={form.handleSubmit(onSubmit)}\n              className=\"rounded-lg border w-full p-4 px-3 md:px-6 focus-within:shadow-sm grid grid-cols-12 gap-2\"\n            >\n              <FormField\n                name=\"prompt\"\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-10\">\n                    <FormControl className=\"m-0 p-0\">\n                      <Input\n                        {...field}\n                        placeholder=\"Start typing here...\"\n                        className=\"border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent\"\n                        disabled={isLoading}\n                      />\n                    </FormControl>\n                  </FormItem>\n                )}\n              />\n              <Button className=\"col-span-12 lg:col-span-2 w-full\" disabled={isLoading}>\n                Generate\n              </Button>\n            </form>\n          </Form>\n        </div>\n        <div className=\"space-y-4 mt-4\">\n          {isLoading && (\n            <div className=\"p-8 rounded-lg w-full flex items-center justify-center bg-muted\">\n              <Loader />\n            </div>\n          )}\n          {messages.length === 0 && !isLoading && <Empty label=\"Start typing to have a conversation.\" />}\n          <div className=\"flex flex-col-reverse gap-y-4\">\n            {messages.map((message, index) => (\n              <div\n                key={index}\n                className={cn(\n                  \"p-8 w-full flex items-start gap-x-8 rounded-lg\",\n                  message.role === \"user\" ? \"bg-white border border-black/10\" : \"bg-muted\"\n                )}\n              >\n                {message.role === \"user\" ? <UserAvatar /> : <BotAvatar />}\n                <ReactMarkdown\n                  className=\"text-sm overflow-hidden leading-7\"\n                  components={{\n                    pre: ({ node, ...props }) => (\n                      <div className=\"overflow-auto w-full my-2 bg-black/10 p-2 rounded-lg\">\n                        <pre {...props} />\n                      </div>\n                    ),\n                    code: ({ node, ...props }) => <code className=\"rounded-sm p-1 bg-black/10\" {...props} />,\n                  }}\n                >\n                  {message.content || \"\"}\n                </ReactMarkdown>\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default CodePage;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/conversation/constants.tsx",
    "content": "import * as z from \"zod\";\n\nexport const formSchema = z.object({\n  prompt: z.string().min(1, {\n    message: \"Prompt is required.\",\n  }),\n});\n"
  },
  {
    "path": "app/(dashboard)/(routes)/conversation/page.tsx",
    "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport axios from \"axios\";\nimport { MessageSquare } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { ChatCompletionRequestMessage } from \"openai\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as z from \"zod\";\n\nimport { BotAvatar } from \"@/components/bot-avatar\";\nimport { Empty } from \"@/components/empty\";\nimport { Heading } from \"@/components/heading\";\nimport { Loader } from \"@/components/loader\";\nimport { Button } from \"@/components/ui/button\";\nimport { Form, FormControl, FormField, FormItem } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { UserAvatar } from \"@/components/user-avatar\";\nimport { cn } from \"@/lib/utils\";\n\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport { toast } from \"react-hot-toast\";\nimport { formSchema } from \"./constants\";\n\nconst ConversationPage = () => {\n  const router = useRouter();\n  const proModal = useProModal();\n  const [messages, setMessages] = useState<ChatCompletionRequestMessage[]>([]);\n\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      prompt: \"\",\n    },\n  });\n\n  const isLoading = form.formState.isSubmitting;\n\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    console.log(values);\n    try {\n      const userMessage: ChatCompletionRequestMessage = {\n        role: \"user\",\n        content: values.prompt,\n      };\n      const newMessages = [...messages, userMessage];\n\n      const response = await axios.post(\"/api/conversation\", {\n        messages: newMessages,\n      });\n\n      setMessages((current) => [...current, userMessage, response.data]);\n      form.reset();\n    } catch (error: any) {\n      console.log(error);\n      if (error?.response?.status === 403) {\n        proModal.onOpen();\n      } else {\n        toast.error(\"Something went wrong.\");\n      }\n    } finally {\n      router.refresh();\n    }\n  };\n\n  return (\n    <div>\n      <Heading\n        title=\"Conversation\"\n        description=\"Our most advanced AI conversation model.\"\n        icon={MessageSquare}\n        iconColor=\"text-violet-500\"\n        bgColor=\"bg-violet-500/10\"\n      />\n      <div className=\"px-4 lg:px-8\">\n        <div>\n          <Form {...form}>\n            <form\n              onSubmit={form.handleSubmit(onSubmit)}\n              className=\"rounded-lg border w-full p-4 px-3 md:px-6 focus-within:shadow-sm grid grid-cols-12 gap-2\"\n            >\n              <FormField\n                name=\"prompt\"\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-10\">\n                    <FormControl className=\"m-0 p-0\">\n                      <Input\n                        {...field}\n                        placeholder=\"Start typing here...\"\n                        className=\"border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent\"\n                        disabled={isLoading}\n                      />\n                    </FormControl>\n                  </FormItem>\n                )}\n              />\n              <Button className=\"col-span-12 lg:col-span-2 w-full\" disabled={isLoading}>\n                Generate\n              </Button>\n            </form>\n          </Form>\n        </div>\n        <div className=\"space-y-4 mt-4\">\n          {isLoading && (\n            <div className=\"p-8 rounded-lg w-full flex items-center justify-center bg-muted\">\n              <Loader />\n            </div>\n          )}\n          {messages.length === 0 && !isLoading && <Empty label=\"Start typing to have a conversation.\" />}\n          <div className=\"flex flex-col-reverse gap-y-4\">\n            {messages.map((message, index) => (\n              <div\n                key={index}\n                className={cn(\n                  \"p-8 w-full flex items-start gap-x-8 rounded-lg\",\n                  message.role === \"user\" ? \"bg-white border border-black/10\" : \"bg-muted\"\n                )}\n              >\n                {message.role === \"user\" ? <UserAvatar /> : <BotAvatar />}\n                <p className=\"text-sm\">{message.content}</p>\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default ConversationPage;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/dashboard/page.tsx",
    "content": "\"use client\";\n\nimport {\n\tArrowRight,\n\tCode,\n\tImageIcon,\n\tMessageSquare,\n\tMusic,\n\tVideoIcon,\n} from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\n\nimport { Card } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\n\nconst tools = [\n\t{\n\t\tlabel: \"Conversation\",\n\t\ticon: MessageSquare,\n\t\tcolor: \"text-violet-500\",\n\t\tbgColor: \"bg-violet-500/10\",\n\t\thref: \"/conversation\",\n\t},\n\t{\n\t\tlabel: \"Music Generation\",\n\t\ticon: Music,\n\t\tcolor: \"text-emerald-500\",\n\t\tbgColor: \"bg-emerald-500/10\",\n\t\thref: \"/music\",\n\t},\n\t{\n\t\tlabel: \"Image Generation\",\n\t\ticon: ImageIcon,\n\t\tcolor: \"text-pink-700\",\n\t\tbgColor: \"bg-pink-700/10\",\n\t\thref: \"/image\",\n\t},\n\t{\n\t\tlabel: \"Video Generation\",\n\t\ticon: VideoIcon,\n\t\tcolor: \"text-orange-700\",\n\t\tbgColor: \"bg-orange-700/10\",\n\t\thref: \"/video\",\n\t},\n\t{\n\t\tlabel: \"Code Generation\",\n\t\ticon: Code,\n\t\tcolor: \"text-green-700\",\n\t\tbgColor: \"bg-green-700/10\",\n\t\thref: \"/code\",\n\t},\n];\n\nconst DashboardPage = () => {\n\tconst router = useRouter();\n\n\treturn (\n\t\t<div>\n\t\t\t<div className=\"mb-8 space-y-4\">\n\t\t\t\t<h2 className=\"text-2xl md:text-4xl font-bold text-center\">\n\t\t\t\t\tExplore the power of AI\n\t\t\t\t</h2>\n\t\t\t\t<p className=\"text-muted-foreground font-light text-sm md:text-lg text-center\">\n\t\t\t\t\tPrometheus is a platform that allows you to generate music, videos,\n\t\t\t\t\tand code using the power of AI.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<div className=\"px-4 md:px-20 lg:px-32 space-y-4\">\n\t\t\t\t{tools.map((tool) => (\n\t\t\t\t\t<Card\n\t\t\t\t\t\tonClick={() => router.push(tool.href)}\n\t\t\t\t\t\tkey={tool.href}\n\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\"p-4 border-black/5 flex items-center justify-between hover:shadow-md transition cursor-pointer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"flex items-center gap-x-4\">\n\t\t\t\t\t\t\t<div className={cn(\"p-2 w-fit rounded-md\", tool.bgColor)}>\n\t\t\t\t\t\t\t\t<tool.icon className={cn(\"w-8 h-8\", tool.color)} />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"font-semibold\">{tool.label}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ArrowRight className=\"w-5 h-5\" />\n\t\t\t\t\t</Card>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nexport default DashboardPage;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/image/constants.tsx",
    "content": "import * as z from \"zod\";\n\nexport const formSchema = z.object({\n\tprompt: z.string().min(1, {\n\t\tmessage: \"Image Prompt is required.\",\n\t}),\n\tamount: z.string().min(1),\n\tresolution: z.string().min(1),\n});\n\nexport const amountOptions = [\n\t{\n\t\tvalue: \"1\",\n\t\tlabel: \"1 Photo\",\n\t},\n\t{\n\t\tvalue: \"2\",\n\t\tlabel: \"2 Photos\",\n\t},\n\t{\n\t\tvalue: \"3\",\n\t\tlabel: \"3 Photos\",\n\t},\n\t{\n\t\tvalue: \"4\",\n\t\tlabel: \"4 Photos\",\n\t},\n\t{\n\t\tvalue: \"5\",\n\t\tlabel: \"5 Photos\",\n\t},\n];\n\nexport const resolutionOptions = [\n\t{\n\t\tvalue: \"256x256\",\n\t\tlabel: \"256x256\",\n\t},\n\t{\n\t\tvalue: \"512x512\",\n\t\tlabel: \"512x512\",\n\t},\n\t{\n\t\tvalue: \"1024x1024\",\n\t\tlabel: \"1024x1024\",\n\t},\n];\n"
  },
  {
    "path": "app/(dashboard)/(routes)/image/page.tsx",
    "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport axios from \"axios\";\nimport { Download, ImageIcon } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as z from \"zod\";\n\nimport { Empty } from \"@/components/empty\";\nimport { Heading } from \"@/components/heading\";\nimport { Loader } from \"@/components/loader\";\nimport { Button } from \"@/components/ui/button\";\nimport { Form, FormControl, FormField, FormItem } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\n\nimport { Card, CardFooter } from \"@/components/ui/card\";\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport Image from \"next/image\";\nimport { toast } from \"react-hot-toast\";\nimport { amountOptions, formSchema, resolutionOptions } from \"./constants\";\n\nconst ImagePage = () => {\n  const router = useRouter();\n  const proModal = useProModal();\n  const [images, setImages] = useState<string[]>([]);\n\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      prompt: \"\",\n      amount: \"1\",\n      resolution: \"512x512\",\n    },\n  });\n\n  const isLoading = form.formState.isSubmitting;\n\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    console.log(values);\n    try {\n      setImages([]);\n      const response = await axios.post(\"/api/image\", values);\n\n      const urls = response.data.map((image: { url: string }) => image.url);\n      setImages(urls);\n\n      form.reset();\n    } catch (error: any) {\n      console.log(error);\n      if (error?.response?.status === 403) {\n        proModal.onOpen();\n      } else {\n        toast.error(\"Something went wrong.\");\n      }\n    } finally {\n      router.refresh();\n    }\n  };\n\n  return (\n    <div>\n      <Heading\n        title=\"Image Generation\"\n        description=\"Our most advanced AI Image Generation model.\"\n        icon={ImageIcon}\n        iconColor=\"text-pink-700\"\n        bgColor=\"bg-pink-700/10\"\n      />\n      <div className=\"px-4 lg:px-8\">\n        <div>\n          <Form {...form}>\n            <form\n              onSubmit={form.handleSubmit(onSubmit)}\n              className=\"rounded-lg border w-full p-4 px-3 md:px-6 focus-within:shadow-sm grid grid-cols-12 gap-2\"\n            >\n              <FormField\n                name=\"prompt\"\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-6\">\n                    <FormControl className=\"m-0 p-0\">\n                      <Input\n                        {...field}\n                        placeholder=\"Start typing here...\"\n                        className=\"border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent\"\n                        disabled={isLoading}\n                      />\n                    </FormControl>\n                  </FormItem>\n                )}\n              />\n              <FormField\n                name=\"amount\"\n                control={form.control}\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-2\">\n                    <Select disabled={isLoading} onValueChange={field.onChange} value={field.value} defaultValue={field.value}>\n                      <FormControl>\n                        <SelectTrigger>\n                          <SelectValue defaultValue={field.value} />\n                        </SelectTrigger>\n                      </FormControl>\n                      <SelectContent>\n                        {amountOptions.map((option) => (\n                          <SelectItem key={option.value} value={option.value}>\n                            {option.label}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  </FormItem>\n                )}\n              />\n              <FormField\n                name=\"resolution\"\n                control={form.control}\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-2\">\n                    <Select disabled={isLoading} onValueChange={field.onChange} value={field.value} defaultValue={field.value}>\n                      <FormControl>\n                        <SelectTrigger>\n                          <SelectValue defaultValue={field.value} />\n                        </SelectTrigger>\n                      </FormControl>\n                      <SelectContent>\n                        {resolutionOptions.map((option) => (\n                          <SelectItem key={option.value} value={option.value}>\n                            {option.label}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                  </FormItem>\n                )}\n              />\n              <Button className=\"col-span-12 lg:col-span-2 w-full\" disabled={isLoading}>\n                Generate\n              </Button>\n            </form>\n          </Form>\n        </div>\n        <div className=\"space-y-4 mt-4\">\n          {isLoading && (\n            <div className=\"p-20\">\n              <Loader />\n            </div>\n          )}\n          {images.length === 0 && !isLoading && <Empty label=\"Start typing to generate images.\" />}\n          <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 mt-8\">\n            {images.map((image, index) => (\n              <Card key={index} className=\"rounded-lg overflow-hidden\">\n                <div className=\"relative aspect-square\">\n                  <Image src={image} fill alt=\"image\" />\n                </div>\n                <CardFooter className=\"p-2\">\n                  <Button onClick={() => window.open(image)} variant=\"secondary\" className=\"w-full\">\n                    <Download className=\"h-4 w-4\" />\n                    Download\n                  </Button>\n                </CardFooter>\n              </Card>\n            ))}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default ImagePage;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/music/constants.tsx",
    "content": "import * as z from \"zod\";\n\nexport const formSchema = z.object({\n  prompt: z.string().min(1, {\n    message: \"Prompt is required.\",\n  }),\n});\n"
  },
  {
    "path": "app/(dashboard)/(routes)/music/page.tsx",
    "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport axios from \"axios\";\nimport { Music } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as z from \"zod\";\n\nimport { Empty } from \"@/components/empty\";\nimport { Heading } from \"@/components/heading\";\nimport { Loader } from \"@/components/loader\";\nimport { Button } from \"@/components/ui/button\";\nimport { Form, FormControl, FormField, FormItem } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport { toast } from \"react-hot-toast\";\nimport { formSchema } from \"./constants\";\n\nconst MusicPage = () => {\n  const router = useRouter();\n  const proModal = useProModal();\n  const [music, setMusic] = useState(\"\");\n\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      prompt: \"\",\n    },\n  });\n\n  const isLoading = form.formState.isSubmitting;\n\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    console.log(values);\n    try {\n      setMusic(\"\");\n      const response = await axios.post(\"/api/music\");\n      setMusic(response.data.audio);\n\n      form.reset();\n    } catch (error: any) {\n      console.log(error);\n      if (error?.response?.status === 403) {\n        proModal.onOpen();\n      } else {\n        toast.error(\"Something went wrong.\");\n      }\n    } finally {\n      router.refresh();\n    }\n  };\n\n  return (\n    <div>\n      <Heading\n        title=\"Music Generation\"\n        description=\"Our most advanced AI Music Generation model.\"\n        icon={Music}\n        iconColor=\"text-emerald-500\"\n        bgColor=\"bg-emerald-500/10\"\n      />\n      <div className=\"px-4 lg:px-8\">\n        <div>\n          <Form {...form}>\n            <form\n              onSubmit={form.handleSubmit(onSubmit)}\n              className=\"rounded-lg border w-full p-4 px-3 md:px-6 focus-within:shadow-sm grid grid-cols-12 gap-2\"\n            >\n              <FormField\n                name=\"prompt\"\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-10\">\n                    <FormControl className=\"m-0 p-0\">\n                      <Input\n                        {...field}\n                        placeholder=\"Start typing here...\"\n                        className=\"border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent\"\n                        disabled={isLoading}\n                      />\n                    </FormControl>\n                  </FormItem>\n                )}\n              />\n              <Button className=\"col-span-12 lg:col-span-2 w-full\" disabled={isLoading}>\n                Generate\n              </Button>\n            </form>\n          </Form>\n        </div>\n        <div className=\"space-y-4 mt-4\">\n          {isLoading && (\n            <div className=\"p-8 rounded-lg w-full flex items-center justify-center bg-muted\">\n              <Loader />\n            </div>\n          )}\n          {!music && !isLoading && <Empty label=\"Start typing to generate music.\" />}\n          {music && (\n            <audio controls className=\"w-full mt-8\">\n              <source src={music} />\n            </audio>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default MusicPage;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/settings/page.tsx",
    "content": "import { Heading } from \"@/components/heading\";\nimport { SubscriptionButton } from \"@/components/subscription-button\";\nimport { checkSubscription } from \"@/lib/subscription\";\nimport { Settings } from \"lucide-react\";\n\nconst SettingsPage = async () => {\n  const isPro = await checkSubscription();\n\n  return (\n    <div>\n      <Heading title=\"Settings\" description=\"Manage account settings.\" icon={Settings} iconColor=\"text-gray-700\" bgColor=\"bg-gray-700/10\" />\n      <div className=\"px-4 lg:px-8 space-y-4\">\n        <div className=\"text-muted-foreground text-sm\">\n          {isPro ? \"You are currently on a pro plan.\" : \"You are currently on a free plan.\"}\n        </div>\n        <SubscriptionButton isPro={isPro} />\n      </div>\n    </div>\n  );\n};\n\nexport default SettingsPage;\n"
  },
  {
    "path": "app/(dashboard)/(routes)/video/constants.tsx",
    "content": "import * as z from \"zod\";\n\nexport const formSchema = z.object({\n  prompt: z.string().min(1, {\n    message: \"Prompt is required.\",\n  }),\n});\n"
  },
  {
    "path": "app/(dashboard)/(routes)/video/page.tsx",
    "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport axios from \"axios\";\nimport { Video } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as z from \"zod\";\n\nimport { Empty } from \"@/components/empty\";\nimport { Heading } from \"@/components/heading\";\nimport { Loader } from \"@/components/loader\";\nimport { Button } from \"@/components/ui/button\";\nimport { Form, FormControl, FormField, FormItem } from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport { toast } from \"react-hot-toast\";\nimport { formSchema } from \"./constants\";\n\nconst VideoPage = () => {\n  const router = useRouter();\n  const proModal = useProModal();\n  const [video, setVideo] = useState(\"\");\n\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      prompt: \"\",\n    },\n  });\n\n  const isLoading = form.formState.isSubmitting;\n\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    console.log(values);\n    try {\n      setVideo(\"\");\n      const response = await axios.post(\"/api/music\");\n      setVideo(response.data.audio);\n\n      form.reset();\n    } catch (error: any) {\n      console.log(error);\n      if (error?.response?.status === 403) {\n        proModal.onOpen();\n      } else {\n        toast.error(\"Something went wrong.\");\n      }\n    } finally {\n      router.refresh();\n    }\n  };\n\n  return (\n    <div>\n      <Heading\n        title=\"Video Generation\"\n        description=\"Our most advanced AI Video Generation model.\"\n        icon={Video}\n        iconColor=\"text-orange-700\"\n        bgColor=\"bg-orange-700/10\"\n      />\n      <div className=\"px-4 lg:px-8\">\n        <div>\n          <Form {...form}>\n            <form\n              onSubmit={form.handleSubmit(onSubmit)}\n              className=\"rounded-lg border w-full p-4 px-3 md:px-6 focus-within:shadow-sm grid grid-cols-12 gap-2\"\n            >\n              <FormField\n                name=\"prompt\"\n                render={({ field }) => (\n                  <FormItem className=\"col-span-12 lg:col-span-10\">\n                    <FormControl className=\"m-0 p-0\">\n                      <Input\n                        {...field}\n                        placeholder=\"Start typing here...\"\n                        className=\"border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent\"\n                        disabled={isLoading}\n                      />\n                    </FormControl>\n                  </FormItem>\n                )}\n              />\n              <Button className=\"col-span-12 lg:col-span-2 w-full\" disabled={isLoading}>\n                Generate\n              </Button>\n            </form>\n          </Form>\n        </div>\n        <div className=\"space-y-4 mt-4\">\n          {isLoading && (\n            <div className=\"p-8 rounded-lg w-full flex items-center justify-center bg-muted\">\n              <Loader />\n            </div>\n          )}\n          {!video && !isLoading && <Empty label=\"Start typing to generate videos.\" />}\n          {video && (\n            <video className=\"w-full aspect-video mt-8 rounded-lg border bg-black\" controls>\n              <source src={video} />\n            </video>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default VideoPage;\n"
  },
  {
    "path": "app/(dashboard)/layout.tsx",
    "content": "import Navbar from \"@/components/navbar\";\nimport Sidebar from \"@/components/sidebar\";\nimport { getApiLimitCount } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\n\nconst DashboardLayout = async ({ children }: { children: React.ReactNode }) => {\n  const apiLimitCount = await getApiLimitCount();\n  const isPro = await checkSubscription();\n\n  return (\n    <div className=\"h-full relative\">\n      <div className=\"hidden h-full md:flex md:flex-col md:fixed md:w-72 md:inset-y-0 bg-gray-900\">\n        <Sidebar apiLimitCount={apiLimitCount} isPro={isPro} />\n      </div>\n      <main className=\"md:pl-72\">\n        <Navbar />\n        {children}\n      </main>\n    </div>\n  );\n};\n\nexport default DashboardLayout;\n"
  },
  {
    "path": "app/(landing)/layout.tsx",
    "content": "const LandingLayout = ({ children }: { children: React.ReactNode }) => {\n  return (\n    <main className=\"h-full bg-[#111827] overflow-auto\">\n      <div className=\"mx-auto max-w-screen-xl h-full\">{children}</div>\n    </main>\n  );\n};\n\nexport default LandingLayout;\n"
  },
  {
    "path": "app/(landing)/page.tsx",
    "content": "import LandingContent from \"@/components/landing-content\";\nimport { LandingHero } from \"@/components/landing-hero\";\nimport { LandingNabvbar } from \"@/components/landing-navbar\";\n\nfunction LandingPage() {\n  return (\n    <div className=\"h-full\">\n      <LandingNabvbar />\n      <LandingHero />\n      <LandingContent />\n    </div>\n  );\n}\n\nexport default LandingPage;\n"
  },
  {
    "path": "app/api/code/route.ts",
    "content": "import { checkApiLimit, increaseApiLimit } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\nimport { auth } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\nimport { ChatCompletionRequestMessage, Configuration, OpenAIApi } from \"openai\";\n\nconst configuration = new Configuration({\n  apiKey: process.env.OPENAI_API_KEY,\n});\n\nconst openAi = new OpenAIApi(configuration);\n\nconst instructionMessage: ChatCompletionRequestMessage = {\n  role: \"system\",\n  content: \"You are a code generator. You must answer only in markdown code snippets. Use code comments for explanations.\",\n};\n\nexport async function POST(req: Request) {\n  try {\n    const { userId } = auth();\n    const body = await req.json();\n    const { messages } = body;\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    if (!configuration) {\n      return new NextResponse(\"OpenAI API Key not configured\", { status: 500 });\n    }\n\n    if (!messages) {\n      return new NextResponse(\"Missing messages\", { status: 400 });\n    }\n\n    const isAllowed = await checkApiLimit();\n    const isPro = await checkSubscription();\n\n    if (!isAllowed && !isPro) {\n      return new NextResponse(\"API Limit Exceeded\", { status: 403 });\n    }\n\n    const response = await openAi.createChatCompletion({\n      model: \"gpt-3.5-turbo\",\n      messages: [instructionMessage, ...messages],\n    });\n\n    if (!isPro) {\n      await increaseApiLimit();\n    }\n\n    return NextResponse.json(response.data.choices[0].message, { status: 200 });\n  } catch (error) {\n    console.log(\"[CODE_ERROR]\", error);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/conversation/route.ts",
    "content": "import { auth } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\nimport { Configuration, OpenAIApi } from \"openai\";\n\nimport { checkApiLimit, increaseApiLimit } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\n\nconst configuration = new Configuration({\n  apiKey: process.env.OPENAI_API_KEY,\n});\n\nconst openAi = new OpenAIApi(configuration);\n\nexport async function POST(req: Request) {\n  try {\n    const { userId } = auth();\n    const body = await req.json();\n    const { messages } = body;\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    if (!configuration) {\n      return new NextResponse(\"OpenAI API Key not configured\", { status: 500 });\n    }\n\n    if (!messages) {\n      return new NextResponse(\"Missing messages\", { status: 400 });\n    }\n\n    const isAllowed = await checkApiLimit();\n    const isPro = await checkSubscription();\n\n    if (!isAllowed && !isPro) {\n      return new NextResponse(\"API Limit Exceeded\", { status: 403 });\n    }\n\n    const response = await openAi.createChatCompletion({\n      model: \"gpt-3.5-turbo\",\n      messages,\n    });\n\n    if (!isPro) {\n      await increaseApiLimit();\n    }\n\n    return NextResponse.json(response.data.choices[0].message, { status: 200 });\n  } catch (error) {\n    console.log(\"[CONVERSATION_ERROR]\", error);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/image/route.ts",
    "content": "import { checkApiLimit, increaseApiLimit } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\nimport { auth } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\nimport { Configuration, OpenAIApi } from \"openai\";\n\nconst configuration = new Configuration({\n  apiKey: process.env.OPENAI_API_KEY,\n});\n\nconst openAi = new OpenAIApi(configuration);\n\nexport async function POST(req: Request) {\n  try {\n    const { userId } = auth();\n    const body = await req.json();\n    const { prompt, amount = 1, resolution = \"512x512\" } = body;\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    if (!configuration) {\n      return new NextResponse(\"OpenAI API Key not configured\", { status: 500 });\n    }\n\n    if (!prompt) {\n      return new NextResponse(\"Missing prompt\", { status: 400 });\n    }\n\n    if (!amount) {\n      return new NextResponse(\"Missing amount\", { status: 400 });\n    }\n\n    if (!resolution) {\n      return new NextResponse(\"Missing resolution\", { status: 400 });\n    }\n\n    const isAllowed = await checkApiLimit();\n    const isPro = await checkSubscription();\n\n    if (!isAllowed && !isPro) {\n      return new NextResponse(\"API Limit Exceeded\", { status: 403 });\n    }\n\n    const response = await openAi.createImage({\n      prompt,\n      n: parseInt(amount, 10),\n      size: resolution,\n    });\n\n    if (!isPro) {\n      await increaseApiLimit();\n    }\n\n    return NextResponse.json(response.data.data, { status: 200 });\n  } catch (error) {\n    console.log(\"[CONVERSATION_ERROR]\", error);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/music/route.ts",
    "content": "import { checkApiLimit, increaseApiLimit } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\nimport { auth } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\nimport Replicate from \"replicate\";\n\nconst replicate = new Replicate({\n  auth: process.env.REPLICATE_API_TOKEN!,\n});\n\nexport async function POST(req: Request) {\n  try {\n    const { userId } = auth();\n    const body = await req.json();\n    const { prompt } = body;\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    if (!prompt) {\n      return new NextResponse(\"Prompt is required\", { status: 400 });\n    }\n\n    const isAllowed = await checkApiLimit();\n    const isPro = await checkSubscription();\n\n    if (!isAllowed && !isPro) {\n      return new NextResponse(\"API Limit Exceeded\", { status: 403 });\n    }\n\n    const response = await replicate.run(\"riffusion/riffusion:8cf61ea6c56afd61d8f5b9ffd14d7c216c0a93844ce2d82ac1c9ecc9c7f24e05\", {\n      input: {\n        prompt_a: prompt,\n      },\n    });\n\n    if (!isPro) {\n      await increaseApiLimit();\n    }\n\n    return NextResponse.json(response, { status: 200 });\n  } catch (error) {\n    console.log(\"[MUSIC_ERROR]\", error);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/stripe/route.ts",
    "content": "import { auth, currentUser } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\n\nimport prismadb from \"@/lib/prismadb\";\nimport { stripe } from \"@/lib/stripe\";\nimport { absoluteUrl } from \"@/lib/utils\";\n\nconst settingsUrl = absoluteUrl(\"/settings\");\n\nexport async function GET() {\n  try {\n    const { userId } = auth();\n    const user = await currentUser();\n\n    if (!userId || !user) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const userSubscription = await prismadb.userSubscription.findUnique({\n      where: {\n        userId,\n      },\n    });\n\n    if (userSubscription && userSubscription.stripeCustomerId) {\n      const stripeSession = await stripe.billingPortal.sessions.create({\n        customer: userSubscription.stripeCustomerId,\n        return_url: settingsUrl,\n      });\n\n      return new NextResponse(JSON.stringify({ url: stripeSession.url }), { status: 200 });\n    }\n\n    const stripeSession = await stripe.checkout.sessions.create({\n      success_url: settingsUrl,\n      cancel_url: settingsUrl,\n      payment_method_types: [\"card\"],\n      mode: \"subscription\",\n      billing_address_collection: \"auto\",\n      customer_email: user.emailAddresses[0].emailAddress,\n      line_items: [\n        {\n          price_data: {\n            currency: \"USD\",\n            product_data: {\n              name: \"Prometheus Pro\",\n              description: \"Prometheus Pro\",\n            },\n            unit_amount: 2000,\n            recurring: {\n              interval: \"month\",\n            },\n          },\n          quantity: 1,\n        },\n      ],\n      metadata: {\n        userId,\n      },\n    });\n\n    return new NextResponse(JSON.stringify({ url: stripeSession.url }), { status: 200 });\n  } catch (error) {\n    console.log(\"[STRIPE_ERROR]\", error);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/video/route.ts",
    "content": "import { checkApiLimit, increaseApiLimit } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\nimport { auth } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\nimport Replicate from \"replicate\";\n\nconst replicate = new Replicate({\n  auth: process.env.REPLICATE_API_TOKEN!,\n});\n\nexport async function POST(req: Request) {\n  try {\n    const { userId } = auth();\n    const body = await req.json();\n    const { prompt } = body;\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    if (!prompt) {\n      return new NextResponse(\"Prompt is required\", { status: 400 });\n    }\n\n    const isAllowed = await checkApiLimit();\n    const isPro = await checkSubscription();\n\n    if (!isAllowed && !isPro) {\n      return new NextResponse(\"API Limit Exceeded\", { status: 403 });\n    }\n\n    const response = await replicate.run(\"anotherjesse/zeroscope-v2-xl:9f747673945c62801b13b84701c783929c0ee784e4748ec062204894dda1a351\", {\n      input: {\n        prompt,\n      },\n    });\n\n    if (!isPro) {\n      await increaseApiLimit();\n    }\n\n    return NextResponse.json(response, { status: 200 });\n  } catch (error) {\n    console.log(\"[VIDEO_ERROR]\", error);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/webhook/route.ts",
    "content": "import { headers } from \"next/headers\";\nimport Stripe from \"stripe\";\n\nimport prismadb from \"@/lib/prismadb\";\nimport { stripe } from \"@/lib/stripe\";\nimport { NextResponse } from \"next/server\";\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(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);\n  } catch (error: any) {\n    return new NextResponse(`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    const subscription = await stripe.subscriptions.retrieve(session.subscription as string);\n\n    if (!session?.metadata?.userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    await prismadb.userSubscription.create({\n      data: {\n        userId: session.metadata.userId,\n        stripeSubscriptionId: subscription.id,\n        stripeCustomerId: subscription.customer as string,\n        stripePriceId: subscription.items.data[0].price.id,\n        stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000),\n      },\n    });\n  }\n\n  if (event.type === \"invoice.payment_succeeded\") {\n    const subscription = await stripe.subscriptions.retrieve(session.subscription as string);\n\n    await prismadb.userSubscription.update({\n      where: {\n        stripeSubscriptionId: subscription.id,\n      },\n      data: {\n        stripePriceId: subscription.items.data[0].price.id,\n        stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000),\n      },\n    });\n  }\n\n  return new NextResponse(\"OK\", { status: 200 });\n}\n"
  },
  {
    "path": "app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml,\nbody,\n:root {\n  height: 100%;\n}\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\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 84% 4.9%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n\n    --primary: 248 90% 66%;\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 84.2% 60.2%;\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: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n\n    --muted: 217.2 32.6% 17.5%;\n    --muted-foreground: 215 20.2% 65.1%;\n\n    --popover: 222.2 84% 4.9%;\n    --popover-foreground: 210 40% 98%;\n\n    --card: 222.2 84% 4.9%;\n    --card-foreground: 210 40% 98%;\n\n    --border: 217.2 32.6% 17.5%;\n    --input: 217.2 32.6% 17.5%;\n\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 11.2%;\n\n    --secondary: 217.2 32.6% 17.5%;\n    --secondary-foreground: 210 40% 98%;\n\n    --accent: 217.2 32.6% 17.5%;\n    --accent-foreground: 210 40% 98%;\n\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 0 85.7% 97.3%;\n\n    --ring: 217.2 32.6% 17.5%;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n"
  },
  {
    "path": "app/layout.tsx",
    "content": "import { ClerkProvider } from \"@clerk/nextjs\";\nimport type { Metadata } from \"next\";\nimport { Inter } from \"next/font/google\";\n\nimport CrispProvider from \"@/components/crisp-provider\";\nimport { ModalProvider } from \"@/components/modal-provider\";\nimport { ToasterProvider } from \"@/components/toaster-provider\";\nimport \"./globals.css\";\n\nconst inter = Inter({ subsets: [\"latin\"] });\n\nexport const metadata: Metadata = {\n  title: \"Prometheus AI\",\n  description: \"An AI platform.\",\n};\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <ClerkProvider>\n      <html lang=\"en\">\n        <body className={inter.className}>\n          <ModalProvider />\n          <ToasterProvider />\n          <CrispProvider />\n          {children}\n        </body>\n      </html>\n    </ClerkProvider>\n  );\n}\n"
  },
  {
    "path": "components/bot-avatar.tsx",
    "content": "import { Avatar, AvatarImage } from \"./ui/avatar\";\n\nexport const BotAvatar = () => {\n\treturn (\n\t\t<Avatar className=\"h-8 w-8\">\n\t\t\t<AvatarImage className=\"p-1\" src=\"/logo.png\" />\n\t\t</Avatar>\n\t);\n};\n"
  },
  {
    "path": "components/crisp-chat.tsx",
    "content": "\"use client\";\n\nimport { Crisp } from \"crisp-sdk-web\";\nimport { useEffect } from \"react\";\n\nexport const CrispChat = () => {\n\tuseEffect(() => {\n\t\tCrisp.configure(process.env.CRISP_WEBSITE_ID!);\n\t}, []);\n\n\treturn null;\n};\n"
  },
  {
    "path": "components/crisp-provider.tsx",
    "content": "import { CrispChat } from \"./crisp-chat\";\n\nexport const CrispProvider = () => {\n  return <CrispChat />;\n};\n\nexport default CrispProvider;\n"
  },
  {
    "path": "components/empty.tsx",
    "content": "import Image from \"next/image\";\n\ninterface EmptyProps {\n\tlabel: string;\n}\n\nexport const Empty = ({ label }: EmptyProps) => {\n\treturn (\n\t\t<div className=\"h-full p-20 flex flex-col items-center justify-center\">\n\t\t\t<div className=\"relative h-72 w-72\">\n\t\t\t\t<Image alt=\"Empty\" src=\"/empty.png\" fill />\n\t\t\t\t<p className=\"text-muted-foreground text-sm text-center\">{label}</p>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n"
  },
  {
    "path": "components/free-counter.tsx",
    "content": "\"use client\";\n\nimport { FC, useEffect, useState } from \"react\";\n\nimport { MAX_FREE_COUNTS } from \"@/constants\";\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport { Zap } from \"lucide-react\";\nimport { Button } from \"./ui/button\";\nimport { Card, CardContent } from \"./ui/card\";\nimport { Progress } from \"./ui/progress\";\n\ninterface FreeCounterProps {\n  apiLimitCount: number;\n  isPro: boolean;\n}\n\nexport const FreeCounter: FC<FreeCounterProps> = ({ apiLimitCount = 0, isPro = false }) => {\n  const [mounted, setMounted] = useState(false);\n  const proModal = useProModal();\n\n  useEffect(() => {\n    setMounted(true);\n  }, []);\n\n  if (!mounted) {\n    return null;\n  }\n\n  if (isPro) {\n    return null;\n  }\n\n  return (\n    <div className=\"px-3\">\n      <Card className=\"bg-white/10 border-0\">\n        <CardContent className=\"py-6\">\n          <div className=\"text-center text-sm text-white mb-4 space-y-2\">\n            <p>\n              {apiLimitCount} / {MAX_FREE_COUNTS} Free Generations\n            </p>\n            <Progress className=\"h-3\" value={(apiLimitCount / MAX_FREE_COUNTS) * 100} />\n          </div>\n          <Button variant=\"premium\" className=\"w-full\" onClick={proModal.onOpen}>\n            Upgrade <Zap className=\"w-4 h-4 ml-2 fill-white\" />\n          </Button>\n        </CardContent>\n      </Card>\n    </div>\n  );\n};\n\nexport default FreeCounter;\n"
  },
  {
    "path": "components/heading.tsx",
    "content": "import { LucideIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface HeadingProps {\n\ttitle: string;\n\tdescription: string;\n\ticon: LucideIcon;\n\ticonColor?: string;\n\tbgColor?: string;\n}\n\nexport const Heading = ({\n\ttitle,\n\tdescription,\n\ticon: Icon,\n\ticonColor,\n\tbgColor,\n}: HeadingProps) => {\n\treturn (\n\t\t<div className=\"px-4 lg:px-8 flex items-center gap-x-3 mb-8\">\n\t\t\t<div className={cn(\"p-2 w-fit rounded-md\", bgColor)}>\n\t\t\t\t<Icon className={cn(\"w-10 h-10\", iconColor)} />\n\t\t\t</div>\n\t\t\t<div>\n\t\t\t\t<h2 className=\"text-3xl font-bold\">{title}</h2>\n\t\t\t\t<p className=\"text-sm text-muted-foreground\">{description}</p>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n"
  },
  {
    "path": "components/landing-content.tsx",
    "content": "\"use client\";\n\nimport { Card, CardContent, CardHeader, CardTitle } from \"./ui/card\";\n\nconst testimonials = [\n  {\n    name: \"John Doe\",\n    avatar: \"A\",\n    title: \"Software Engineer\",\n    description: \"This is the best application I've ever used!\",\n  },\n  {\n    name: \"John Doe\",\n    avatar: \"A\",\n    title: \"Software Engineer\",\n    description: \"This is the best application I've ever used!\",\n  },\n  {\n    name: \"John Doe\",\n    avatar: \"A\",\n    title: \"Software Engineer\",\n    description: \"This is the best application I've ever used!\",\n  },\n  {\n    name: \"John Doe\",\n    avatar: \"A\",\n    title: \"Software Engineer\",\n    description: \"This is the best application I've ever used!\",\n  },\n];\n\nexport const LandingContent = () => {\n  return (\n    <div className=\"px-10 pb-20\">\n      <h2 className=\"text-center text-4xl text-white font-extrabold mb-10\">Testimonials</h2>\n      <div className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4\">\n        {testimonials.map((item) => (\n          <Card key={item.description} className=\"bg-[#192339] border-none text-white\">\n            <CardHeader>\n              <CardTitle className=\"flex items-center gap-x-2\">\n                <div>\n                  <p className=\"text-lg \">{item.name}</p>\n                  <p className=\"text-zinc-400 text-sm\">{item.title}</p>\n                </div>\n              </CardTitle>\n              <CardContent className=\"pt-4 px-0\">{item.description}</CardContent>\n            </CardHeader>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default LandingContent;\n"
  },
  {
    "path": "components/landing-hero.tsx",
    "content": "\"use client\";\n\nimport { useAuth } from \"@clerk/nextjs\";\nimport Link from \"next/link\";\nimport TypewriterComponent from \"typewriter-effect\";\n\nimport { Button } from \"./ui/button\";\n\nexport const LandingHero = () => {\n  const { isSignedIn } = useAuth();\n  return (\n    <div className=\"text-white font-bold py-36 text-center space-y-5\">\n      <div className=\"text-4xl sm:text-5xl md:text-6xl lg:text-7xl space-y-5 font-extrabold\">\n        <h1>The Best AI Tool for</h1>\n        <div className=\"text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-600\">\n          <TypewriterComponent\n            options={{\n              strings: [\"Chatbot.\", \"Photo Generation.\", \"Music Generation.\", \"Code Generation.\", \"Video Generation.\"],\n              autoStart: true,\n              loop: true,\n            }}\n          />\n        </div>\n      </div>\n      <div className=\"text-sm md:text-xl font-light text-zinc-400\">Create content using the power of AI.</div>\n      <div>\n        <Link href={isSignedIn ? \"/dashboard\" : \"/sign-up\"}>\n          <Button variant=\"premium\" className=\"md:textlg p-4 md:p-6 rounded-full font-semibold\">\n            Start Generating For Free\n          </Button>\n        </Link>\n      </div>\n      <div className=\"text-zinc-400 text-xs md:text-sm font-normal\">No credit card required. Cancel anytime.</div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "components/landing-navbar.tsx",
    "content": "\"use client\";\n\nimport { useAuth } from \"@clerk/nextjs\";\nimport { Montserrat } from \"next/font/google\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"./ui/button\";\n\nconst font = Montserrat({\n  weight: \"600\",\n  subsets: [\"latin\"],\n});\n\nexport const LandingNabvbar = () => {\n  const { isSignedIn } = useAuth();\n\n  return (\n    <nav className=\"p-4 bg-transparent flex items-center justify-between\">\n      <Link href=\"/\" className=\"flex items-center\">\n        <div className=\"relative h-8 w-8 mr-4\">\n          <Image fill src=\"/logo.png\" alt=\"Logo\" />\n        </div>\n        <h1 className={cn(\"text-2xl font-bold text-white\", font.className)}>Prometheus</h1>\n      </Link>\n      <div className=\"flex items-center gap-x-2\">\n        <Link href={isSignedIn ? \"/dashboard\" : \"/sign-up\"}>\n          <Button variant=\"outline\" className=\"rounded-full\">\n            Get Started\n          </Button>\n        </Link>\n      </div>\n    </nav>\n  );\n};\n"
  },
  {
    "path": "components/loader.tsx",
    "content": "import Image from \"next/image\";\n\nexport const Loader = () => {\n\treturn (\n\t\t<div className=\"h-full flex flex-col gap-y-4 items-center justify-center\">\n\t\t\t<div className=\"w-10 h-10 relative animate-spin\">\n\t\t\t\t<Image alt=\"Logo\" src=\"/logo.png\" fill />\n\t\t\t</div>\n\t\t\t<p className=\"text-muted-foreground text-sm\">Prometheus is thinking...</p>\n\t\t</div>\n\t);\n};\n"
  },
  {
    "path": "components/mobile-sidebar.tsx",
    "content": "\"use client\";\n\nimport { Menu } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport Sidebar from \"./sidebar\";\nimport { Button } from \"./ui/button\";\nimport { Sheet, SheetContent, SheetTrigger } from \"./ui/sheet\";\n\nconst MobileSidebar = ({ apiLimitCount = 0, isPro = false }: { apiLimitCount: number; isPro: boolean }) => {\n  const [isMounted, setIsMounted] = useState(false);\n\n  useEffect(() => {\n    setIsMounted(true);\n  }, []);\n\n  if (!isMounted) {\n    return null;\n  }\n\n  return (\n    <Sheet>\n      <SheetTrigger>\n        <Button variant=\"ghost\" size=\"icon\" className=\"md:hidden\">\n          <Menu />\n        </Button>\n      </SheetTrigger>\n      <SheetContent side=\"left\" className=\"p-0\">\n        <Sidebar apiLimitCount={apiLimitCount} isPro={isPro} />\n      </SheetContent>\n    </Sheet>\n  );\n};\n\nexport default MobileSidebar;\n"
  },
  {
    "path": "components/modal-provider.tsx",
    "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport ProModal from \"./pro-modal\";\n\nexport const ModalProvider = () => {\n  const [mounted, setMounted] = useState(false);\n\n  useEffect(() => {\n    setMounted(true);\n  }, []);\n\n  if (!mounted) {\n    return null;\n  }\n\n  return (\n    <>\n      <ProModal />\n    </>\n  );\n};\n"
  },
  {
    "path": "components/navbar.tsx",
    "content": "import { UserButton } from \"@clerk/nextjs\";\n\nimport { getApiLimitCount } from \"@/lib/api-limit\";\nimport { checkSubscription } from \"@/lib/subscription\";\nimport MobileSidebar from \"./mobile-sidebar\";\n\nconst Navbar = async () => {\n  const apiLimitCount = await getApiLimitCount();\n  const isPro = await checkSubscription();\n\n  return (\n    <div className=\"flex items-center p-4\">\n      <MobileSidebar apiLimitCount={apiLimitCount} isPro={isPro} />\n      <div className=\"flex w-full justify-end\">\n        <UserButton afterSignOutUrl=\"/\" />\n      </div>\n    </div>\n  );\n};\n\nexport default Navbar;\n"
  },
  {
    "path": "components/pro-modal.tsx",
    "content": "\"use client\";\n\nimport axios from \"axios\";\nimport { useState } from \"react\";\n\nimport useProModal from \"@/hooks/use-pro-modal\";\nimport { cn } from \"@/lib/utils\";\nimport { Check, Code, ImageIcon, MessageSquare, Music, VideoIcon, Zap } from \"lucide-react\";\nimport { toast } from \"react-hot-toast\";\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Card } from \"./ui/card\";\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from \"./ui/dialog\";\n\nexport const ProModal = () => {\n  const proModal = useProModal();\n  const [loading, setLoading] = useState(false);\n\n  const tools = [\n    {\n      label: \"Conversation\",\n      icon: MessageSquare,\n      color: \"text-violet-500\",\n      bgColor: \"bg-violet-500/10\",\n    },\n    {\n      label: \"Music Generation\",\n      icon: Music,\n      color: \"text-emerald-500\",\n      bgColor: \"bg-emerald-500/10\",\n    },\n    {\n      label: \"Image Generation\",\n      icon: ImageIcon,\n      color: \"text-pink-700\",\n      bgColor: \"bg-pink-700/10\",\n    },\n    {\n      label: \"Video Generation\",\n      icon: VideoIcon,\n      color: \"text-orange-700\",\n      bgColor: \"bg-orange-700/10\",\n    },\n    {\n      label: \"Code Generation\",\n      icon: Code,\n      color: \"text-green-700\",\n      bgColor: \"bg-green-700/10\",\n    },\n  ];\n\n  const onSubscribe = async () => {\n    try {\n      setLoading(true);\n      const response = await axios.get(\"/api/stripe\");\n\n      window.location.href = response.data.url;\n    } catch (error) {\n      console.log(\"[STRIPE_CLIENT_ERROR]\", error);\n      toast.error(\"Something went wrong.\");\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  return (\n    <Dialog open={proModal.isOpen} onOpenChange={proModal.onClose}>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle className=\"flex justify-center flex-col items-center pb-2 gap-y-4\">\n            <div className=\"flex items-center gap-x-2 font-bold py-1\">\n              Upgrade to Prometheus Pro\n              <Badge variant=\"premium\" className=\"uppercase text-sm py-1\">\n                pro\n              </Badge>\n            </div>\n          </DialogTitle>\n          <DialogDescription className=\"text-center pt-2 space-y-2 text-zinc-900 font-medium\">\n            {tools.map((tool) => (\n              <Card key={tool.label} className=\"p-3 border-black/5 flex items-center justify-between\">\n                <div className=\"flex items-center gap-x-4\">\n                  <div className={cn(\"p-2 flex w-fit rounded-md\", tool.bgColor)}>\n                    <tool.icon className={cn(\"h-6 w-6\", tool.color)} />\n                  </div>\n                  <div className=\"font-semibold text-sm\">{tool.label}</div>\n                </div>\n                <Check className=\"text-primary w-5 h-5\" />\n              </Card>\n            ))}\n          </DialogDescription>\n        </DialogHeader>\n        <DialogFooter>\n          <Button disabled={loading} size=\"lg\" variant=\"premium\" className=\"w-full\" onClick={onSubscribe}>\n            Upgrade <Zap className=\"w-4 h-4 ml-2 fill-white\" />\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nexport default ProModal;\n"
  },
  {
    "path": "components/sidebar.tsx",
    "content": "\"use client\";\n\nimport { Code, ImageIcon, LayoutDashboard, MessageSquare, Music, Settings, VideoIcon } from \"lucide-react\";\nimport { Montserrat } from \"next/font/google\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport { FC } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport FreeCounter from \"./free-counter\";\n\nconst montserrat = Montserrat({ weight: \"600\", subsets: [\"latin\"] });\n\nconst routes = [\n  {\n    label: \"Dashboard\",\n    icon: LayoutDashboard,\n    href: \"/dashboard\",\n    color: \"text-sky-500\",\n  },\n  {\n    label: \"Conversation\",\n    icon: MessageSquare,\n    href: \"/conversation\",\n    color: \"text-violet-500\",\n  },\n  {\n    label: \"Image Generation\",\n    icon: ImageIcon,\n    href: \"/image\",\n    color: \"text-pink-700\",\n  },\n  {\n    label: \"Video Generation\",\n    icon: VideoIcon,\n    href: \"/video\",\n    color: \"text-orange-700\",\n  },\n  {\n    label: \"Music Generation\",\n    icon: Music,\n    href: \"/music\",\n    color: \"text-emerald-500\",\n  },\n  {\n    label: \"Code Generation\",\n    icon: Code,\n    href: \"/code\",\n    color: \"text-green-700\",\n  },\n  {\n    label: \"Settings\",\n    icon: Settings,\n    href: \"/settings\",\n  },\n];\n\ninterface SidebarProps {\n  apiLimitCount: number;\n  isPro: boolean;\n}\n\nconst Sidebar: FC<SidebarProps> = ({ apiLimitCount = 0, isPro = false }) => {\n  const pathname = usePathname();\n\n  return (\n    <div className=\"space-y-4 py-4 flex flex-col h-full bg-[#111827] text-white\">\n      <div className=\"px-3 py-2 flex-1\">\n        <Link href=\"/dashboard\" className=\"flex items-center pl-3 mb-14\">\n          <div className=\"relative w-8 h-8 mr-4\">\n            <Image fill alt=\"Logo\" src=\"/logo.png\" />\n          </div>\n          <h1 className={cn(\"text-2xl font-bold\", montserrat.className)}>Prometheus</h1>\n        </Link>\n        <div className=\"space-y-1\">\n          {routes.map((route) => (\n            <Link\n              href={route.href}\n              key={route.href}\n              className={cn(\n                \"text-sm group flex p-3 w-full justify-start font-medium cursor-pointer hover:text-white hover:bg-white/10 rounded-lg transition\",\n                pathname === route.href ? \"bg-white/10 text-white\" : \"text-zinc-400\"\n              )}\n            >\n              <div className=\"flex items-center flex-1\">\n                <route.icon className={cn(\"w-5 h-5 mr-3\", route.color)} />\n                {route.label}\n              </div>\n            </Link>\n          ))}\n        </div>\n      </div>\n      <FreeCounter apiLimitCount={apiLimitCount} isPro={isPro} />\n    </div>\n  );\n};\n\nexport default Sidebar;\n"
  },
  {
    "path": "components/subscription-button.tsx",
    "content": "\"use client\";\n\nimport axios from \"axios\";\nimport { Zap } from \"lucide-react\";\nimport { FC, useState } from \"react\";\nimport { toast } from \"react-hot-toast\";\nimport { Button } from \"./ui/button\";\n\ninterface SubscriptionButtonProps {\n  isPro: boolean;\n}\n\nexport const SubscriptionButton: FC<SubscriptionButtonProps> = ({ isPro = false }) => {\n  const [loading, setLoading] = useState(false);\n\n  const onClick = async () => {\n    try {\n      setLoading(true);\n      const response = await axios.get(\"/api/stripe\");\n\n      window.location.href = response.data.url;\n    } catch (error) {\n      console.log(\"[BILLING_ERROR]\", error);\n      toast.error(\"Something went wrong.\");\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  return (\n    <Button disabled={loading} variant={isPro ? \"default\" : \"premium\"} onClick={onClick}>\n      {isPro ? \"Manage Subscription\" : \"Upgrade to Pro\"}\n      {!isPro && <Zap className=\"w-4 h-4 ml-2 fill-white\" />}\n    </Button>\n  );\n};\n"
  },
  {
    "path": "components/toaster-provider.tsx",
    "content": "\"use client\";\n\nimport { Toaster } from \"react-hot-toast\";\n\nexport const ToasterProvider = () => {\n  return <Toaster />;\n};\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 { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst badgeVariants = cva(\n  \"inline-flex items-center rounded-full border 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: \"border-transparent bg-primary text-primary-foreground hover:bg-primary/80\",\n        secondary: \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        destructive: \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",\n        outline: \"text-foreground\",\n        premium: \"bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-primary-foreground border-0\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n);\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n  return <div className={cn(badgeVariants({ variant }), className)} {...props} />;\n}\n\nexport { Badge, badgeVariants };\n"
  },
  {
    "path": "components/ui/button.tsx",
    "content": "import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium 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  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n        premium: \"bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-white border-0\",\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n  asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, asChild = false, ...props }, ref) => {\n  const Comp = asChild ? Slot : \"button\";\n  return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;\n});\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\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-2xl 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/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  ...props\n}: DialogPrimitive.DialogPortalProps) => (\n  <DialogPrimitive.Portal className={cn(className)} {...props} />\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 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\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 left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full\",\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/form.tsx",
    "content": "import * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport {\n  Controller,\n  ControllerProps,\n  FieldPath,\n  FieldValues,\n  FormProvider,\n  useFormContext,\n} from \"react-hook-form\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Label } from \"@/components/ui/label\"\n\nconst Form = FormProvider\n\ntype FormFieldContextValue<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> = {\n  name: TName\n}\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>(\n  {} as FormFieldContextValue\n)\n\nconst FormField = <\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>({\n  ...props\n}: ControllerProps<TFieldValues, TName>) => {\n  return (\n    <FormFieldContext.Provider value={{ name: props.name }}>\n      <Controller {...props} />\n    </FormFieldContext.Provider>\n  )\n}\n\nconst useFormField = () => {\n  const fieldContext = React.useContext(FormFieldContext)\n  const itemContext = React.useContext(FormItemContext)\n  const { getFieldState, formState } = useFormContext()\n\n  const fieldState = getFieldState(fieldContext.name, formState)\n\n  if (!fieldContext) {\n    throw new Error(\"useFormField should be used within <FormField>\")\n  }\n\n  const { id } = itemContext\n\n  return {\n    id,\n    name: fieldContext.name,\n    formItemId: `${id}-form-item`,\n    formDescriptionId: `${id}-form-item-description`,\n    formMessageId: `${id}-form-item-message`,\n    ...fieldState,\n  }\n}\n\ntype FormItemContextValue = {\n  id: string\n}\n\nconst FormItemContext = React.createContext<FormItemContextValue>(\n  {} as FormItemContextValue\n)\n\nconst FormItem = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const id = React.useId()\n\n  return (\n    <FormItemContext.Provider value={{ id }}>\n      <div ref={ref} className={cn(\"space-y-2\", className)} {...props} />\n    </FormItemContext.Provider>\n  )\n})\nFormItem.displayName = \"FormItem\"\n\nconst FormLabel = React.forwardRef<\n  React.ElementRef<typeof LabelPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>\n>(({ className, ...props }, ref) => {\n  const { error, formItemId } = useFormField()\n\n  return (\n    <Label\n      ref={ref}\n      className={cn(error && \"text-destructive\", className)}\n      htmlFor={formItemId}\n      {...props}\n    />\n  )\n})\nFormLabel.displayName = \"FormLabel\"\n\nconst FormControl = React.forwardRef<\n  React.ElementRef<typeof Slot>,\n  React.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n  const { error, formItemId, formDescriptionId, formMessageId } = useFormField()\n\n  return (\n    <Slot\n      ref={ref}\n      id={formItemId}\n      aria-describedby={\n        !error\n          ? `${formDescriptionId}`\n          : `${formDescriptionId} ${formMessageId}`\n      }\n      aria-invalid={!!error}\n      {...props}\n    />\n  )\n})\nFormControl.displayName = \"FormControl\"\n\nconst FormDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n  const { formDescriptionId } = useFormField()\n\n  return (\n    <p\n      ref={ref}\n      id={formDescriptionId}\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n})\nFormDescription.displayName = \"FormDescription\"\n\nconst FormMessage = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n  const { error, formMessageId } = useFormField()\n  const body = error ? String(error?.message) : children\n\n  if (!body) {\n    return null\n  }\n\n  return (\n    <p\n      ref={ref}\n      id={formMessageId}\n      className={cn(\"text-sm font-medium text-destructive\", className)}\n      {...props}\n    >\n      {body}\n    </p>\n  )\n})\nFormMessage.displayName = \"FormMessage\"\n\nexport {\n  useFormField,\n  Form,\n  FormItem,\n  FormLabel,\n  FormControl,\n  FormDescription,\n  FormMessage,\n  FormField,\n}\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-background 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 { cva, type VariantProps } 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/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/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 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n        position === \"popper\" &&\n          \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-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/sheet.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { cva, type VariantProps } 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 SheetClose = SheetPrimitive.Close\n\nconst SheetPortal = ({\n  className,\n  ...props\n}: SheetPrimitive.DialogPortalProps) => (\n  <SheetPrimitive.Portal className={cn(className)} {...props} />\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, ...props }, ref) => (\n  <SheetPrimitive.Overlay\n    className={cn(\n      \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\nconst sheetVariants = cva(\n  \"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n  {\n    variants: {\n      side: {\n        top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n        bottom:\n          \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n        left: \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n        right:\n          \"inset-y-0 right-0 h-full w-3/4  border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n      },\n    },\n    defaultVariants: {\n      side: \"right\",\n    },\n  }\n)\n\ninterface SheetContentProps\n  extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n    VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Content>,\n  SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n  <SheetPortal>\n    <SheetOverlay />\n    <SheetPrimitive.Content\n      ref={ref}\n      className={cn(sheetVariants({ side }), 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  SheetClose,\n  SheetContent,\n  SheetHeader,\n  SheetFooter,\n  SheetTitle,\n  SheetDescription,\n}\n"
  },
  {
    "path": "components/user-avatar.tsx",
    "content": "import { useUser } from \"@clerk/nextjs\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"./ui/avatar\";\n\nexport const UserAvatar = () => {\n\tconst { user } = useUser();\n\treturn (\n\t\t<Avatar className=\"h-8 w-8\">\n\t\t\t<AvatarImage src={user?.profileImageUrl} />\n\t\t\t<AvatarFallback>\n\t\t\t\t{user?.firstName?.charAt(0)}\n\t\t\t\t{user?.lastName?.charAt(0)}\n\t\t\t</AvatarFallback>\n\t\t</Avatar>\n\t);\n};\n"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.js\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"slate\",\n    \"cssVariables\": true\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\"\n  }\n}"
  },
  {
    "path": "constants.ts",
    "content": "export const MAX_FREE_COUNTS = 5;\n"
  },
  {
    "path": "hooks/use-pro-modal.tsx",
    "content": "import { create } from \"zustand\";\n\ninterface useProModalStore {\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}\n\nexport const useProModal = create<useProModalStore>((set) => ({\n  isOpen: false,\n  onOpen: () => set({ isOpen: true }),\n  onClose: () => set({ isOpen: false }),\n}));\n\nexport default useProModal;\n"
  },
  {
    "path": "lib/api-limit.ts",
    "content": "import { auth } from \"@clerk/nextjs\";\n\nimport { MAX_FREE_COUNTS } from \"@/constants\";\nimport prismadb from \"./prismadb\";\n\nexport const increaseApiLimit = async () => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return;\n  }\n\n  const userApiLimit = await prismadb.userApiLimit.findUnique({\n    where: {\n      userId,\n    },\n  });\n\n  if (!userApiLimit) {\n    await prismadb.userApiLimit.create({\n      data: {\n        userId,\n        count: 1,\n      },\n    });\n  } else {\n    await prismadb.userApiLimit.update({\n      where: {\n        userId,\n      },\n      data: {\n        count: userApiLimit.count + 1,\n      },\n    });\n  }\n};\n\nexport const checkApiLimit = async () => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return false;\n  }\n\n  const userApiLimit = await prismadb.userApiLimit.findUnique({\n    where: {\n      userId,\n    },\n  });\n\n  if (!userApiLimit || userApiLimit.count < MAX_FREE_COUNTS) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\nexport const getApiLimitCount = async () => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return 0;\n  }\n\n  const userApiLimit = await prismadb.userApiLimit.findUnique({\n    where: {\n      userId,\n    },\n  });\n\n  if (!userApiLimit) {\n    return 0;\n  } else {\n    return userApiLimit.count;\n  }\n};\n"
  },
  {
    "path": "lib/prismadb.ts",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\ndeclare global {\n  var prisma: PrismaClient | undefined;\n}\n\nconst prismadb = globalThis.prisma || new PrismaClient();\nif (process.env.NODE_ENV !== \"production\") globalThis.prisma = prismadb;\n\nexport default prismadb;\n"
  },
  {
    "path": "lib/stripe.ts",
    "content": "import Stripe from \"stripe\";\n\nexport const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {\n  apiVersion: \"2022-11-15\",\n  typescript: true,\n});\n"
  },
  {
    "path": "lib/subscription.ts",
    "content": "import { auth } from \"@clerk/nextjs\";\n\nimport prismadb from \"./prismadb\";\n\nconst DAY_IN_MS = 1000 * 60 * 60 * 24;\n\nexport const checkSubscription = async () => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return false;\n  }\n\n  const userSubscription = await prismadb.userSubscription.findUnique({\n    where: {\n      userId,\n    },\n    select: {\n      stripeSubscriptionId: true,\n      stripeCurrentPeriodEnd: true,\n      stripeCustomerId: true,\n      stripePriceId: true,\n    },\n  });\n\n  if (!userSubscription) {\n    return false;\n  }\n\n  const isValid = userSubscription.stripePriceId && userSubscription.stripeCurrentPeriodEnd?.getTime()! + DAY_IN_MS > Date.now();\n\n  return !!isValid;\n};\n"
  },
  {
    "path": "lib/utils.ts",
    "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nexport function absoluteUrl(path: string) {\n  return `${process.env.NEXT_PUBLIC_APP_URL}${path}`;\n}\n"
  },
  {
    "path": "middleware.ts",
    "content": "import { authMiddleware } from \"@clerk/nextjs\";\n\n// This example protects all routes including api/trpc routes\n// Please edit this to allow other routes to be public as needed.\n// See https://clerk.com/docs/nextjs/middleware for more information about configuring your middleware\nexport default authMiddleware({\n  publicRoutes: [\"/\", \"/api/webhook\"],\n});\n\nexport const config = {\n  matcher: [\"/((?!.*\\\\..*|_next).*)\", \"/\", \"/(api|trpc)(.*)\"],\n};\n"
  },
  {
    "path": "next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n\timages: {\n\t\tdomains: [\"oaidalleapiprodscus.blob.core.windows.net\"],\n\t},\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ai-saas\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@clerk/nextjs\": \"^4.23.0\",\n    \"@hookform/resolvers\": \"^3.1.1\",\n    \"@prisma/client\": \"^5.0.0\",\n    \"@radix-ui/react-avatar\": \"^1.0.3\",\n    \"@radix-ui/react-dialog\": \"^1.0.4\",\n    \"@radix-ui/react-label\": \"^2.0.2\",\n    \"@radix-ui/react-progress\": \"^1.0.3\",\n    \"@radix-ui/react-select\": \"^1.2.2\",\n    \"@radix-ui/react-slot\": \"^1.0.2\",\n    \"@types/node\": \"20.4.3\",\n    \"@types/react\": \"18.2.15\",\n    \"@types/react-dom\": \"18.2.7\",\n    \"autoprefixer\": \"10.4.14\",\n    \"axios\": \"^1.4.0\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.0.0\",\n    \"crisp-sdk-web\": \"^1.0.19\",\n    \"eslint\": \"8.45.0\",\n    \"eslint-config-next\": \"13.4.12\",\n    \"lucide-react\": \"^0.263.0\",\n    \"next\": \"13.4.12\",\n    \"openai\": \"^3.3.0\",\n    \"postcss\": \"8.4.27\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-hook-form\": \"^7.45.2\",\n    \"react-hot-toast\": \"^2.4.1\",\n    \"react-markdown\": \"^8.0.7\",\n    \"replicate\": \"^0.12.3\",\n    \"stripe\": \"^12.16.0\",\n    \"tailwind-merge\": \"^1.14.0\",\n    \"tailwindcss\": \"3.3.3\",\n    \"tailwindcss-animate\": \"^1.0.6\",\n    \"typescript\": \"5.1.6\",\n    \"typewriter-effect\": \"^2.20.1\",\n    \"zod\": \"^3.21.4\",\n    \"zustand\": \"^4.3.9\"\n  },\n  \"devDependencies\": {\n    \"prisma\": \"^5.0.0\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "prisma/schema.prisma",
    "content": "generator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"mysql\"\n  url = env(\"DATABASE_URL\")\n  relationMode = \"prisma\"\n}\n\nmodel UserApiLimit {\n  id String @id @default(cuid())\n  userId String @unique\n  count Int @default(0)\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel UserSubscription {\n  id String @id @default(cuid())\n  userId String @unique\n  stripeCustomerId String? @unique @map(name: \"stripe_customer_id\")\n  stripeSubscriptionId String? @unique @map(name: \"stripe_subscription_id\")\n  stripePriceId String? @unique @map(name: \"stripe_price_id\")\n  stripeCurrentPeriodEnd DateTime? @map(name: \"stripe_current_period_end\")\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}"
  },
  {
    "path": "tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: [\"class\"],\n  content: [\n    './pages/**/*.{ts,tsx}',\n    './components/**/*.{ts,tsx}',\n    './app/**/*.{ts,tsx}',\n    './src/**/*.{ts,tsx}',\n\t],\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      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\")],\n}"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  }
]