[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": [\"next/core-web-vitals\"]\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\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\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n\npublic/presentations\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Xiaoqian Zhang\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": "<h1 align=\"center\">Welcome to RevealJS AI 👋</h1>\n\n[中文说明](/README_CN.md)\n\n## ✨ Demo\n[/public/demo.mp4](https://github.com/user-attachments/assets/d5a4b37a-553b-41b4-ba33-ad457d118311)\n\nTry it Online ⚡️:  [Revealjs AI](https://ppt.revealjs.online)\n\n## 🚀 Getting Started\n\n### install\n```sh\npnpm install\n```\n### set environmental values\nset .env.local under root dir with values list below\n```sh\nNEXT_PUBLIC_SUPABASE_URL=\nNEXT_PUBLIC_SUPABASE_ANON_KEY=\nKV_REST_API_URL=\nKV_REST_API_TOKEN=\nGEMINI_API_KEY=\n```\n### usege\n```sh\npnpm run dev\n```\n\n## 💻 TechStack\n- [Nextjs](https://nextjs.org/docs) - Full Stack Development\n- [Tailwindcss](https://tailwindcss.com/) - CSS Engine\n- [Supabase](https://supabase.com/) - User OAuth\n- [Stripe](https://stripe.com/docs/development) - Payment\n\n## 💗  Credit\n- [Gemini API](https://gemini.google.com/app) - AI Powered\n- [ai-artifacts](https://github.com/e2b-dev/ai-artifacts) - Reference\n\n## 👤 Author\n**YOYZHANG**\n\n- Twitter: [@alexu19049062](https://twitter.com/alexuzhang19049062)\n- Github: [@YOYZHANG](https://github.com/YOYZHANG)\n- Wechat: whdxzxq\n\n## 🤝 Contributing\n\nContributions, issues and feature requests are welcome. 😄<br />\nFeel free to check [issues page](https://github.com/YOYZHANG/ai-ppt/issues) if you want to contribute.<br />\n\n\n## 📝 License\nMIT License © 2024 YOYZHANG\n\n## Others\n\nPlease ⭐️ this repository if this project helped you!\n\nYour appreciation is my greatest strength in updating content！\n\n<a href=\"https://www.buymeacoffee.com/zhangxiaoqian\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"41\" width=\"174\"></a>\n\n---\n"
  },
  {
    "path": "README_CN.md",
    "content": "<h1 align=\"center\">欢迎来到 RevealJS AI 👋</h1>\n\n## ✨ 示例\n[/public/demo.mp4](https://github.com/user-attachments/assets/d5a4b37a-553b-41b4-ba33-ad457d118311)\n\n在线地址: ⚡️ [Revealjs AI](https://ppt.revealjs.online)\n\n\n## 🚀 快速开始\n\n### 安装依赖\n```sh\npnpm install\n```\n### 设置环境变量\nset .env.local under root dir with values list below\n```sh\nNEXT_PUBLIC_SUPABASE_URL=\nNEXT_PUBLIC_SUPABASE_ANON_KEY=\nKV_REST_API_URL=\nKV_REST_API_TOKEN=\nGEMINI_API_KEY=\n```\n### 本地开发\n```sh\npnpm run dev\n```\n\n## 💻 技术栈\n- [Nextjs](https://nextjs.org/docs) - Full Stack Development\n- [Tailwindcss](https://tailwindcss.com/) - CSS Engine\n- [Supabase](https://supabase.com/) - User OAuth\n- [Stripe](https://stripe.com/docs/development) - Payment\n\n##  💗 感谢以下项目\n- [ai-artifacts](https://github.com/e2b-dev/ai-artifacts) - Reference\n- [Gemini API](https://gemini.google.com/app) - AI Powered\n\n## 👤作者\n\n如果有任何疑问或技术上的交流，可以在 Twitter 或微信上联系我。\n\n**YOYZHANG**\n\n- twitter: [@alexu19049062](https://twitter.com/alexuzhang19049062)\n- 微信: whdxzxq\n\n## 🤝 贡献\n欢迎贡献 [issues](https://github.com/YOYZHANG/ai-ppt/issues).\n如果这个项目对你有帮助，欢迎 ⭐️ 或 Fork.\n\n\n## 📝 License\nMIT License © 2024 YOYZHANG\n\n## 👀 其他\n你的赞赏是我更新内容最大的功力：\n\n<a href=\"https://www.buymeacoffee.com/zhangxiaoqian\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"41\" width=\"174\"></a>\n\n\n---\n\n"
  },
  {
    "path": "app/api/chat/route.ts",
    "content": "import {\n  streamObject,\n  LanguageModel,\n  CoreMessage,\n} from 'ai'\nimport { createGoogleGenerativeAI } from '@ai-sdk/google';\nimport {htmlTemplate} from '@/lib/template'\n\nimport ratelimit from '@/lib/ratelimit'\nimport { artifactSchema } from '@/lib/schema'\n\nexport type LLMModel = {\n  id: string\n  name: string\n  provider: string\n  providerId: string\n}\n\nexport type LLMModelConfig = {\n  model?: string\n  apiKey?: string\n  baseURL?: string\n  temperature?: number\n  topP?: number\n  topK?: number\n  frequencyPenalty?: number\n  presencePenalty?: number\n  maxTokens?: number\n}\n\ninterface Req {\n  messages: CoreMessage[],\n  userID: string,\n}\n\nexport async function POST(req: Request) {\n  const { messages } = await req.json() as Req\n\n  const client = createGoogleGenerativeAI({ apiKey: process.env.GEMINI_API_KEY})('models/gemini-1.5-flash-latest')\n  const stream = await streamObject({\n    model: client as LanguageModel,\n    schema: artifactSchema,\n    system: `\n      Generate a visually appealing reveal.js presentation in HTML.\n      The presentation should include the following slides: appealing cover, bullet points with links, conclusion and end page.\n      more than 6 slides.\n      use the template: ${htmlTemplate}\n    `,\n    messages\n  })\n\n  return stream.toTextStreamResponse()\n}\n"
  },
  {
    "path": "app/api/convertd/route.ts",
    "content": "import { supabase } from '@/lib/supabase';\n\nasync function uploadFileContent(fileContent: string, fileName: string) {\n  const { data, error } = await supabase.storage\n    .from('ppt')\n    .upload(`public/${fileName}`, fileContent, {\n      contentType: 'text/html',\n    })\n\n  if (error) {\n    console.error('covert file failed', error)\n    return null\n  }\n\n  const { publicUrl } = supabase.storage.from('ppt').getPublicUrl(data.path).data\n  \n  return publicUrl\n}\n\nexport async function POST(req: Request) {\n  const { artifact} = await req.json()\n  const url = await uploadFileContent(artifact.code, `ppt_${Date.now()}.html`)\n\n  if (!url) {\n      return new Response('upload ppt html failed.', {\n        status: 403\n      })\n  }\n\n  // Send file URL back to client\n  return new Response(JSON.stringify({\n    url\n  }))\n}\n"
  },
  {
    "path": "app/api/limit/route.ts",
    "content": "import ratelimit from '@/lib/ratelimit'\n\nexport const maxDuration = 60\n\nconst rateLimitMaxRequests = 10\nconst ratelimitWindow = '1d'\n\n\nexport async function GET(req: Request) {\n  const limit = await ratelimit(req.headers.get('x-forwarded-for'), rateLimitMaxRequests, ratelimitWindow)\n\n  if (limit && !limit?.success) {\n    return new Response('You have reached your request limit for the day.', {\n      status: 429,\n      headers: {\n        'X-RateLimit-Limit': limit.amount.toString(),\n        'X-RateLimit-Remaining': limit.remaining.toString(),\n        'X-RateLimit-Reset': limit.reset.toString()\n      }\n    })\n  }\n\n  return new Response(JSON.stringify(limit))\n}\n"
  },
  {
    "path": "app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 240, 6%, 10%;\n    --foreground: 0 0% 98%;\n    --card: 240 10% 3.9%;\n    --card-foreground: 0 0% 98%;\n    --popover: 240, 5%, 13%;\n    --popover-foreground: 0 0% 98%;\n    --primary: 0 0% 98%;\n    --primary-foreground: 240 5.9% 10%;\n    --secondary: 240 3.7% 15.9%;\n    --secondary-foreground: 0 0% 98%;\n    --muted: 240 3.7% 15.9%;\n    --muted-foreground: 240 5% 64.9%;\n    --accent: 240 3.7% 15.9%;\n    --accent-foreground: 0 0% 98%;\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 0 0% 98%;\n    --border: 270, 2%, 19%;\n    --input: 240 3.7% 15.9%;\n    --ring: 240 4.9% 83.9%;\n    --chart-1: 220 70% 50%;\n    --chart-2: 160 60% 45%;\n    --chart-3: 30 80% 55%;\n    --chart-4: 280 65% 60%;\n    --chart-5: 340 75% 55%;\n    --radius: 0.5rem;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n\n"
  },
  {
    "path": "app/layout.tsx",
    "content": "import type { Metadata } from \"next\";\nimport \"./globals.css\";\nimport { Inter } from 'next/font/google'\nimport { PostHogProvider } from './provider'\nimport { ToastContainer } from \"react-toastify\";\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport const metadata: Metadata = {\n  title: \"AI RevealJS\",\n  description: \"Generate RevealJS PPT by AI\",\n};\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode;\n}>) {\n  return (\n    <html lang=\"en\">\n      <PostHogProvider>\n        <body className={inter.className}>\n          <ToastContainer theme=\"dark\"/>\n          {children}\n        </body>\n      </PostHogProvider>\n    </html>\n  );\n}\n"
  },
  {
    "path": "app/page.tsx",
    "content": "'use client'\n\nimport Chat from '@/components/chat'\nimport SideView from '@/components/side-view'\nimport NavBar from '@/components/navbar'\n\nimport { AuthViewType, useAuth } from '@/lib/auth'\nimport { useEffect, useState } from 'react'\nimport { useLocalStorage } from 'usehooks-ts'\nimport { ChatMessage, toAISDKMessages } from '@/lib/messages'\nimport { experimental_useObject as useObject } from 'ai/react'\nimport {ArtifactSchema, artifactSchema } from '@/lib/schema'\nimport { usePostHog } from 'posthog-js/react'\nimport { supabase } from '@/lib/supabase'\nimport { AuthDialog } from '@/components/auth-dialog'\nimport { PriceDialog } from '@/components/price-dialog'\nimport { toast } from 'react-toastify'\n\nexport default function Home() {\n  const posthog = usePostHog()\n  const [currentTab, setCurrentTab] = useState<'code' | 'artifact'>('code')\n  const [isPreviewLoading, setIsPreviewLoading] = useState(false)\n  const [artifact, setArtifact] = useState<Partial<ArtifactSchema> | undefined>()\n  const [authView, setAuthView] = useState<AuthViewType>('sign_in')\n  const [isAuthDialogOpen, setAuthDialog] = useState(false)\n  const [isPriceDialogOpen, setPriceDialogOpen] = useState(false)\n  const { session, apiKey } = useAuth(setAuthDialog, setAuthView)\n\n  const { object, submit, isLoading, stop } = useObject({\n    api: '/api/chat',\n    schema: artifactSchema,\n    onFinish: async ({ object: artifact, error }) => {\n      if (error) {\n        return\n      }\n      setCurrentTab('artifact')\n      setIsPreviewLoading(false)\n    }\n  })\n\n  useEffect(() => {\n    if (object) {\n      setArtifact(object as ArtifactSchema)\n      const lastAssistantMessage = messages.findLast(message => message.role === 'assistant')\n      if (lastAssistantMessage) {\n        lastAssistantMessage.content = [{ type: 'text', text: object.commentary || '' }, { type: 'code', text: object.code || '' }]\n        lastAssistantMessage.meta = {\n          title: object.title,\n          description: object.description\n        }\n      }\n    }\n  }, [object])\n\n  \n  const logout = () => {\n    supabase.auth.signOut()\n  }\n\n  const checkLimit = async (): Promise<boolean> => {\n    const res = await fetch('/api/limit', {\n      method: 'GET',\n    });\n\n    if (res.status === 429) {\n      toast.error('You have reached your request limit for the day')\n      return true\n    }\n\n    return false\n  }\n\n  const [chatInput, setChatInput] = useLocalStorage('chat', '')\n  const handleSaveInputChange = (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {\n    setChatInput(e.target.value)\n  }\n\n  const [messages, setMessages] = useState<ChatMessage[]>([])\n  const addMessage = (message: ChatMessage) => {\n    setMessages(previousMessages => [...previousMessages, message])\n    return [...messages, message]\n  }\n  const handleSubmitAuth = async (e?: React.FormEvent<HTMLFormElement>) => {\n    e?.preventDefault()\n\n    if (!session) {\n      return setAuthDialog(true)\n    }\n\n\n    if (isLoading) {\n      stop()\n    }\n\n    const limited = await checkLimit()\n    if (limited) {\n      return\n    }\n\n    const content: ChatMessage['content'] = [{ type: 'text', text: chatInput }]\n\n    submit({\n      userID: session?.user?.id,\n      messages: toAISDKMessages(addMessage({role: 'user', content})),\n    })\n\n    addMessage({\n      role: 'assistant',\n      content: [{ type: 'text', text: 'Generating RevealJS ppt...' }],\n    })\n\n    setChatInput('')\n    setCurrentTab('code')\n    setIsPreviewLoading(true)\n\n    posthog.capture('chat_submit')\n  }\n\n  return (\n    <main className=\"flex min-h-screen max-h-screen\">\n      {<>\n        <AuthDialog open={isAuthDialogOpen} setOpen={setAuthDialog} view={authView} supabase={supabase} />\n        <PriceDialog open={isPriceDialogOpen} setOpen={setPriceDialogOpen}></PriceDialog>\n      </>\n      }\n      <NavBar\n        session={session}\n        showLogin={() => setAuthDialog(true)}\n        signOut={logout}\n        showPrice={() => setPriceDialogOpen(true)}\n      />\n\n      <div className=\"flex-1 flex space-x-8 w-full pt-16 pb-8 px-4\">\n        <Chat\n           isLoading={isLoading}\n           handleSubmit={handleSubmitAuth}\n           input={chatInput}\n           setChatInput={setChatInput}\n           handleInputChange={handleSaveInputChange}\n           messages={messages}\n        />\n        <SideView\n          isLoading={isPreviewLoading}\n          selectedTab={currentTab}\n          onSelectedTabChange={setCurrentTab}\n          artifact={artifact}\n        />\n      </div>\n    </main>\n  )\n}\n"
  },
  {
    "path": "app/provider.tsx",
    "content": "'use client'\nimport posthog from 'posthog-js'\nimport { PostHogProvider as PostHogProviderJS } from 'posthog-js/react'\n\nposthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY ?? '', {\n  api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,\n  person_profiles: 'identified_only',\n  session_recording: {\n    recordCrossOriginIframes: true,\n  }\n})\n\nexport function PostHogProvider({ children }: { children: React.ReactNode }) {\n  return (\n    <PostHogProviderJS client={posthog}>\n      {children}\n    </PostHogProviderJS>\n  )\n}\n"
  },
  {
    "path": "components/artifact-view.tsx",
    "content": "'use client'\n\nimport { useEffect, useState } from \"react\"\n\ninterface ArtifactViewProps {\n  result: string\n}\n\n\nexport function ArtifactView({\n  result,\n}: ArtifactViewProps) {\n\n  const [iframeKey, setIframeKey] = useState(0);\n\n  useEffect(() => {\n    setIframeKey(prevKey => prevKey + 1);\n  }, [result]);\n\n  if (!result) return null\n\n  const encodedHTML = encodeURIComponent(result);\n  const dataURI = `data:text/html;charset=utf-8,${encodedHTML}`;\n\n  return (\n    <div className=\"w-full h-full\">\n      <iframe\n        key={iframeKey}\n        className=\"h-full w-full\"\n        sandbox=\"allow-forms allow-scripts allow-same-origin\"\n        loading=\"lazy\"\n        src= {dataURI}\n      />\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/auth-dialog.tsx",
    "content": "import {\n  Dialog,\n  DialogContent,\n  DialogTitle,\n} from \"@/components/ui/dialog\"\nimport AuthForm from \"./auth-form\"\nimport { SupabaseClient } from \"@supabase/supabase-js\"\nimport { AuthViewType } from \"@/lib/auth\"\n\nexport function AuthDialog({ open, setOpen, supabase, view }: { open: boolean, setOpen: (open: boolean) => void, supabase: SupabaseClient, view: AuthViewType }) {\n  return (\n    <Dialog open={open} onOpenChange={setOpen}>\n      <DialogContent>\n        <DialogTitle></DialogTitle>\n        <AuthForm supabase={supabase} view={view} />\n      </DialogContent>\n    </Dialog>\n  )\n}\n"
  },
  {
    "path": "components/auth-form.tsx",
    "content": "import { AuthViewType } from '@/lib/auth'\nimport { Auth } from '@supabase/auth-ui-react'\nimport {\n  ThemeSupa\n} from '@supabase/auth-ui-shared'\nimport { SupabaseClient } from '@supabase/supabase-js'\n\nfunction AuthForm({ supabase, view = 'sign_in' }: { supabase: SupabaseClient, view: AuthViewType }) {\n  return (\n    <div className=\"mx-auto flex flex-1 w-full justify-center items-center flex-col\">\n      <h1 className=\"text-4xl font-bold mt-8 mb-4\">\n        Sign in\n      </h1>\n      <div className=\"md:w-[420px] w-[240px]\">\n        <Auth\n          supabaseClient={supabase}\n          appearance={{\n            theme: ThemeSupa,\n            variables: {\n              default: {\n                colors: {\n                  brand: 'rgb(255, 136, 0)',\n                  brandAccent: 'rgb(255, 136, 0)',\n                  inputText: '#FFF',\n                },\n                radii: {\n                  borderRadiusButton: '20px',\n                  inputBorderRadius: '12px'\n                }\n              },\n            },\n          }}\n          localization={{\n            variables: {\n              sign_in: {\n                email_label: 'Email',\n                password_label: 'Password',\n              },\n            },\n          }}\n          view={view}\n          theme='default'\n          showLinks={true}\n          providers={['google', 'github']}\n          providerScopes={{\n            github: 'email',\n            google: 'email'\n          }}\n        />\n      </div>\n    </div>\n  )\n}\n\nexport default AuthForm\n"
  },
  {
    "path": "components/chat.tsx",
    "content": "import { ChangeEvent, FormEvent, useEffect } from 'react'\nimport { ArrowUp, Square, Sparkles,Terminal, BadgeDollarSign} from 'lucide-react'\nimport { ChatMessage } from '@/lib/messages'\nimport { Button } from '@/components/ui/button'\nimport Welcome from './welcome'\nimport { Input } from './ui/input'\n\ninterface ChatProps {\n  isLoading: boolean,\n  handleSubmit: (e?: FormEvent<HTMLFormElement>) => void,\n  setChatInput: (input: string) => void\n  input: string\n  handleInputChange: (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void,\n  messages: ChatMessage[]\n}\n\nexport default function Chat({\n  isLoading,\n  input,\n  setChatInput,\n  messages,\n  handleInputChange,\n  handleSubmit,\n}: ChatProps) {\n  useEffect(() => {\n    const chatContainer = document.getElementById('chat-container')\n    if (chatContainer) {\n      chatContainer.scrollTop = chatContainer.scrollHeight\n    }\n  }, [JSON.stringify(messages)])\n\n  return (\n    <div className=\"flex-1 flex flex-col gap-4 max-h-full max-w-[800px] mx-auto justify-between\">\n      {!!messages.length && (\n        <>\n          <div id=\"chat-container\" className=\"flex flex-col gap-2 overflow-y-auto max-h-full px-4 rounded-lg\">\n            {messages.map((message: ChatMessage, index: number) => (\n              <div className={`py-2 px-4 shadow-sm whitespace-pre-wrap ${message.role !== 'user' ? 'bg-white/5 border text-muted-foreground' : 'bg-white/20'} rounded-lg font-serif`} key={index}>\n                {message.content.map((content, id) => {\n                  if (content.type === 'text') {\n                    return <p key={content.text} className=\"flex-1\">{content.text}</p>\n                  }\n\n                  if (content.type === 'image') {\n                    return <img key={id} src={content.image} alt=\"artifact\" className=\"mr-2 inline-block w-[50px] h-[50px] object-contain border border-[#FFE7CC] rounded-lg bg-white mt-2\" />\n                  }\n                })}\n                {message.meta &&\n                  <div className=\"mt-4 flex justify-start items-start border rounded-md\">\n                    <div className=\"p-2 self-stretch border-r w-14 flex items-center justify-center\">\n                      <Terminal strokeWidth={2} className=\"text-[#FF8800]\"/>\n                    </div>\n                    <div className=\"p-2 flex flex-col space-y-1 justify-start items-start min-w-[100px]\">\n                      <span className=\"font-bold font-sans text-sm text-primary\">{message.meta.title}</span>\n                      <span className=\"font-sans text-sm\">{message.meta.description}</span>\n                    </div>\n                  </div>\n                }\n              </div>\n            ))}\n          </div>\n\n          <div className=\"flex flex-col gap-2\">\n            <form onSubmit={handleSubmit} className=\"flex flex-row gap-2 items-center\">\n              <Input\n                className=\"focus:outline-none resize-none\"\n                required={true}\n                placeholder=\"Describe your ppt...\"\n                value={input}\n                onChange={handleInputChange}\n              />\n              { !isLoading ? (\n                  <Button variant=\"secondary\" size=\"icon\" className='rounded-full h-10 w-11'>\n                    <Sparkles className=\"h-5 w-5 text-[#c5f955]\" />\n                  </Button>\n              ) : (\n                  <Button variant=\"secondary\" size=\"icon\" className='rounded-full h-10 w-11' onClick={(e) => { e.preventDefault(); stop() }}>\n                    <Square className=\"h-5 w-5 text-[#c5f955]\" />\n                  </Button>\n                )\n              }\n            </form>\n          </div>\n        </>\n      )}\n      {!messages.length && \n        <Welcome\n          onSubmit={handleSubmit}\n          onChange={handleInputChange}\n          setChatInput={setChatInput}\n          value={input}\n        />}\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/code-view.tsx",
    "content": "import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { tomorrow } from 'react-syntax-highlighter/dist/esm/styles/prism';\n\nexport function CodeView({ content }: { content: string }) {\n  return (\n    <pre\n      className=\"p-4\"\n      style={{\n        fontSize: 12,\n        backgroundColor: \"transparent\",\n        borderRadius: 0,\n        margin: 0,\n      }}\n    >\n       <SyntaxHighlighter\n        language=\"html\"\n        style={tomorrow}\n        showLineNumbers={false}\n        wrapLines={true}\n      >\n        {content}\n      </SyntaxHighlighter>\n    </pre>\n  );\n}\n"
  },
  {
    "path": "components/navbar.tsx",
    "content": "import Link from 'next/link'\nimport Image from 'next/image'\nimport { Separator } from '@/components/ui/separator'\nimport { Button } from './ui/button'\nimport { Session } from '@supabase/supabase-js'\nimport { User } from '@/components/user'\nimport { BsGithub, BsTwitterX } from \"react-icons/bs\";\n\ninterface NavBarProps {\n  session: Session | null,\n  showLogin: () => void,\n  signOut: () => void,\n  showPrice: () => void\n}\n\nexport default function NavBar({session, signOut, showLogin, showPrice}: NavBarProps) {\n  return (\n    <nav className=\"fixed top-0 left-0 right-0 bg-background\">\n      <div className=\"flex px-4 py-2\">\n        <div className=\"flex flex-1 items-center\">\n          <Link href=\"/\" className=\"flex items-center gap-2\" target=\"_blank\">\n            <Image src=\"/logo.svg\" alt=\"logo\" width={24} height={24}/>\n            <h1 className=\"whitespace-pre ml-2 font-bold\">RevealJS AI </h1>\n          </Link>\n        </div>\n        <div className=\"flex justify-end space-x-4\">\n          <Button variant=\"secondary\" className='text-[#c5f955]' onClick={showPrice}>\n            <span className=\"flex items-center h-5 w-5\">\n              <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\"><path fillRule=\"evenodd\" clipRule=\"evenodd\" d=\"M13.232 2.287A.75.75 0 0 1 13.75 3v6.25H19a.75.75 0 0 1 .607 1.191l-8 11a.75.75 0 0 1-1.357-.44v-6.25H5a.75.75 0 0 1-.607-1.192l8-11a.75.75 0 0 1 .839-.272Z\" fill=\"currentColor\"></path></svg>\n            </span>\n            <span>Subscribe to Pro</span>\n          </Button>\n          <Separator orientation=\"vertical\" />\n          <Button variant=\"ghost\" className='rounded-md px-0 mx-0 text-muted-foreground h-full'>\n            <a href=\"https://github.com/YOYZHANG/ai-ppt\" target=\"_blank\"><BsGithub className=\"text-sm w-5 h-5\" /></a>\n          </Button>\n          <Button variant=\"ghost\" className='rounded-md px-0 mx-0 text-muted-foreground h-full'>\n            <a href=\"https://x.com/alexu19049062\" target=\"_blank\"><BsTwitterX className=\"text-sm w-5 h-5\" /></a>\n          </Button>\n          <Separator orientation=\"vertical\" />\n          <User session={session} signOut={signOut} showLogin={showLogin}></User>\n        </div>\n      </div>\n    </nav>\n  )\n}\n"
  },
  {
    "path": "components/price-dialog.tsx",
    "content": "import {\n  Dialog,\n  DialogContent,\n  DialogTitle,\n} from \"@/components/ui/dialog\"\nimport Price from \"./price\"\n\nexport function PriceDialog({ open, setOpen }: { open: boolean, setOpen: (open: boolean) => void}) {\n  return (\n    <Dialog open={open} onOpenChange={setOpen}>\n      <DialogTitle></DialogTitle>\n      <DialogContent style={{'maxWidth': \"800px\"}}>\n        <Price/>\n      </DialogContent>\n    </Dialog>\n  )\n}\n"
  },
  {
    "path": "components/price.tsx",
    "content": "import React, { useState } from 'react';\nimport { Card, CardContent, CardFooter, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { Check, X } from 'lucide-react';\nimport { toast } from 'react-toastify';\nimport 'react-toastify/dist/ReactToastify.css';\n\nexport interface Plan {\n  name: string\n  description: string\n  price: number\n  credits: number\n  features: {\n    name: string\n    included: boolean\n  }[]\n}\n\nconst PricingPlans = () => {\n  const [isYearly, setIsYearly] = useState(false);\n  const [loading, setLoading] = useState(false);\n\n  const plans: Plan[] = [\n    {\n      name: \"Free\",\n      description: \"No credit card needed\",\n      price: 0,\n      credits: 20,\n      features: [\n        { name: \"20 credits per month\", included: true },\n        { name: \"1 task waiting in queue\", included: true },\n        { name: \"Limited queue priority\", included: true },\n        { name: \"Assets are under CC BY 4.0 license\", included: true },\n      ],\n    },\n    {\n      name: \"Pro\",\n      description: \"Best for individual creators\",\n      price: 20,\n      credits: 100,\n      features: [\n        { name: \"1,000 credits per month\", included: true },\n        { name: \"10 tasks waiting in queue\", included: true },\n        { name: \"Standard queue priority\", included: true },\n        { name: \"Assets are private & customer owned\", included: true },\n      ],\n    },\n  ];\n\n  const handleSubscribe = async (plan: Plan) => {\n    try {\n      if (!process.env.STRIPE_PRIVATE_KEY)  {\n        console.error('xxxxxx')\n        toast(\"👷 Feature Coming Soon....\");\n        return\n      }\n      setLoading(true);\n\n      const response = await fetch(\"/api/stripe\", {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify(plan),\n      });\n\n      const { message, data } = await response.json();\n    \n      if (!data) {\n        setLoading(false);\n        toast.error(message);\n\n        return;\n      }\n    } catch (e) {\n      setLoading(false);\n      toast.error(\"checkout failed\");\n    }\n  };\n\n\n  return (\n    <div className=\" text-white p-8 mx-auto\">\n      <h2 className=\"text-3xl font-bold mb-8 text-center\">Upgrade Your Plan</h2>\n      <div className=\"flex justify-center items-center mb-8 space-x-4\">\n        <span className={`${!isYearly ? 'text-green-400' : 'text-gray-400'}`}>Monthly</span>\n        <Switch checked={isYearly} onCheckedChange={setIsYearly} />\n        <span className={`${isYearly ? 'text-green-400' : 'text-gray-400'}`}>Yearly <span className=\"text-green-400 text-sm\">Save 20%</span></span>\n      </div>\n      <div className=\"grid grid-cols-1 md:grid-cols-1 lg:grid-cols-2 gap-8\">\n        {plans.map((plan) => (\n          <Card key={plan.name} className=\"bg-gray-800 border-gray-700\">\n            <CardHeader>\n              <CardTitle className={`text-2xl font-bold ${plan.name === 'Free' ? 'text-green-400' : plan.name === 'Pro' ? 'text-blue-400' : plan.name === 'Max' ? 'text-purple-400' : 'text-yellow-400'}`}>\n                {plan.name}\n              </CardTitle>\n              <p className=\"text-sm text-gray-400\">{plan.description}</p>\n            </CardHeader>\n            <CardContent>\n              <p className=\"text-4xl font-bold mb-4\">${isYearly ? plan.price * 12 * 0.8 : plan.price}<span className=\"text-sm font-normal text-gray-400\">/{isYearly ? 'year' : 'month'}</span></p>\n              {plan.credits && <p className=\"text-sm text-gray-400 mb-4\">${(plan.price / plan.credits).toFixed(2)} / 100 credits</p>}\n              <ul className=\"space-y-2\">\n                {plan.features.map((feature, index) => (\n                  <li key={index} className=\"flex items-start\">\n                    {feature.included ? (\n                      <Check className=\"mr-2 h-5 w-5 text-green-400\" />\n                    ) : (\n                      <X className=\"mr-2 h-5 w-5 text-red-400\" />\n                    )}\n                    <span className={feature.included ? 'text-gray-200' : 'text-gray-500'}>{feature.name}</span>\n                  </li>\n                ))}\n              </ul>\n            </CardContent>\n            <CardFooter>\n              <Button disabled={loading || plan.name === 'Free'} className=\"w-full bg-green-500 hover:bg-green-600 text-white\" onClick={() => handleSubscribe(plan)}>\n                {plan.name === 'Free' ? 'Current plan' : 'Subscribe Now'}\n              </Button>\n            </CardFooter>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default PricingPlans;\n"
  },
  {
    "path": "components/share-dialog.tsx",
    "content": "import {\n  Dialog,\n  DialogContent,\n  DialogTitle,\n} from \"@/components/ui/dialog\"\nimport { ShareLink } from \"./share\"\n\nexport function ShareDialog({ open, setOpen, url }: { open: boolean, setOpen: (open: boolean) => void, url?: string }) {\n  return (\n    <Dialog open={open} onOpenChange={setOpen}>\n      <DialogContent>\n        <DialogTitle></DialogTitle>\n        <ShareLink url={url}/>\n      </DialogContent>\n    </Dialog>\n  )\n}\n"
  },
  {
    "path": "components/share.tsx",
    "content": "import React from 'react';\nimport {\n  FacebookShareButton,\n  LinkedinShareButton,\n  RedditShareButton,\n  TelegramShareButton,\n  TwitterShareButton,\n} from \"react-share\";\nimport {BsX, BsFacebook, BsReddit, BsLinkedin, BsTelegram} from 'react-icons/bs'\nimport { toast } from 'react-toastify';\nimport { Button } from './ui/button';\nimport { Input } from './ui/input';\n\nconst Title = `Just published a presentation generated by @alexu19049062 to the community.\n#RevealJS  #AI #createdwithai `\n\nexport function ShareLink({url}: {url?: string}) {\n\n  if (!url) {\n    return\n  }\n  function copy(url: string) {\n    navigator.clipboard.writeText(url)\n      .then(() => {\n        toast('Copied to clipboard')\n      })\n      .catch(err => {\n        toast.error('Failed to copy: ' + url)\n      })\n  }\n  return (\n    <div className=\" text-white rounded-lg mx-auto\">\n      <div className=\"flex mb-6\">\n        <div className=\"flex flex-col gap-4 justify-start\">\n          <div className='flex flex-row gap-2 items-center justify-start'>\n            <h3 className=\"text-sm font-semibold mr3 w-[100px]\">By Link: </h3>\n            <Input\n                type=\"text\"\n                value={url}\n                readOnly\n                className=\"flex-grow bg-gray-800 rounded-l-md px-3 py-2 text-sm\"\n              />\n              <Button variant=\"secondary\" onClick={() => copy(url)}>\n                Copy Link\n              </Button>\n          </div>\n          \n          <div className=\"flex flex-row gap-2 items-center justify-start\">\n            <h3 className=\"text-sm font-semibold mr3\">Share To:</h3>\n            <div className=\"flex space-x-4\">\n                <TwitterShareButton url={url} title={Title}>\n                  <BsX color=\"gray\" size={24} />\n                </TwitterShareButton>\n                <FacebookShareButton url={url} title={Title}>\n                  <BsFacebook color=\"gray\" size={24}/>\n                </FacebookShareButton>\n                <LinkedinShareButton url={url} title={Title}>\n                  <BsLinkedin color=\"gray\" size={24} />\n                </LinkedinShareButton>\n                <RedditShareButton url={url} title={Title}>\n                  <BsReddit size={24} color=\"gray\"/>\n                </RedditShareButton>\n                <TelegramShareButton url={url} title={Title}>\n                  <BsTelegram size={24} color=\"gray\"/>\n                </TelegramShareButton>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\n"
  },
  {
    "path": "components/side-view.tsx",
    "content": "import { Dispatch, SetStateAction, useState } from 'react'\nimport { Download, LoaderCircle, Share2, Copy } from 'lucide-react'\n\nimport {\n  Tabs,\n  TabsContent,\n  TabsList,\n  TabsTrigger,\n} from '@/components/ui/tabs'\nimport { Button } from \"@/components/ui/button\"\nimport { CodeView } from './code-view'\nimport { ArtifactView } from './artifact-view'\nimport { ArtifactSchema } from '@/lib/schema'\nimport { toast } from 'react-toastify'\n\ninterface SideViewProps {\n  isLoading:boolean,\n  selectedTab: 'code' | 'artifact'\n  onSelectedTabChange: Dispatch<SetStateAction<\"code\" | \"artifact\">>\n  artifact?: Partial<ArtifactSchema>\n}\n\nexport default function SideView({\n  isLoading,\n  selectedTab,\n  onSelectedTabChange,\n  artifact\n\n}: SideViewProps) {\n  const [isShareDialogOpen, setShareDialogOpen] = useState(false)\n\n  if (!artifact) {\n    return null\n  }\n\n  // function share() {\n  //   setShareDialogOpen(true)\n  // }\n\n  function copy (content: string) {\n    navigator.clipboard.writeText(content)\n      .then(() => {\n        toast('Copied to clipboard')\n      })\n      .catch(err => {\n        toast.error('Failed to copy: ' + content)\n      })\n  }\n\n  function download (content: string) {\n    const blob = new Blob([content], { type: 'text/plain' })\n    const url = window.URL.createObjectURL(blob)\n    const a = document.createElement('a')\n    a.style.display = 'none'\n    a.href = url\n    a.download = \"revealjs.html\"\n    document.body.appendChild(a)\n    a.click()\n    window.URL.revokeObjectURL(url)\n    document.body.removeChild(a)\n  }\n\n  return (\n    <div className=\"flex-1 flex flex-col shadow-2xl rounded-lg border max-w-[800px] bg-popover\">\n      {/* <ShareDialog open={isShareDialogOpen} setOpen={setShareDialogOpen} url={result?.url}></ShareDialog> */}\n      <Tabs\n        value={selectedTab}\n        onValueChange={(value) => onSelectedTabChange(value as 'code' | 'artifact')}\n        className=\"h-full max-h-full overflow-hidden flex flex-col items-start justify-start\"\n      >\n        <div className=\"w-full p-2 grid grid-cols-3 items-center justify-end rounded-t-lg border-b\">\n          <div className='flex justify-start'>\n            {isLoading && <LoaderCircle className=\"h-4 w-4 text-[#a1a1aa] animate-spin\" />}\n          </div>\n\n          <div className='flex justify-center'>\n            <TabsList className=\"px-1 py-0 border h-8\">\n              <TabsTrigger className=\"font-normal text-xs py-1 px-2\" value=\"code\">code</TabsTrigger>\n              <TabsTrigger disabled={!artifact} className=\"font-normal text-xs py-1 px-2\" value=\"artifact\">Preview</TabsTrigger>\n            </TabsList>\n          </div>\n          <div className='flex items-center justify-end space-x-2'>\n          {\n            artifact && (\n              <>\n                <Button variant=\"ghost\" className='h-8 rounded-md px-3 text-muted-foreground' title='Download Artifact' onClick={() => download(artifact.code || '')}>\n                  <Download className=\"h-4 w-4\" />\n                </Button>\n                <Button variant=\"ghost\" className='h-8 rounded-md px-3 text-muted-foreground' title='Copy URL' onClick={() => copy(artifact.code || '')}>\n                  <Copy className=\"h-4 w-4\" />\n                </Button>\n                {/* <Button variant=\"ghost\" className='h-8 rounded-md px-3 text-muted-foreground' title='Share' onClick={() => share()}>\n                  <Share2 className=\"h-4 w-4\" />\n                </Button> */}\n              </>\n            )\n          }\n          </div>\n        </div>\n\n        <div className=\"w-full flex-1 flex flex-col items-start justify-start overflow-y-auto\">\n          {artifact && (\n            <>\n              <TabsContent value=\"code\" className=\"flex-1 w-full\">\n                {artifact.code &&\n                  <CodeView content={artifact.code}/>\n                }\n              </TabsContent>\n              <TabsContent value=\"artifact\" className=\"flex-1 w-full flex flex-col items-start justify-start\">\n                {artifact &&\n                  <ArtifactView\n                    result={artifact?.code || ''}\n                  />\n                }\n              </TabsContent>\n            </>\n          )}\n        </div>\n      </Tabs>\n    </div>\n  )\n}\n"
  },
  {
    "path": "components/ui/alert.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst alertVariants = cva(\n  \"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-background text-foreground\",\n        destructive:\n          \"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst Alert = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n>(({ className, variant, ...props }, ref) => (\n  <div\n    ref={ref}\n    role=\"alert\"\n    className={cn(alertVariants({ variant }), className)}\n    {...props}\n  />\n))\nAlert.displayName = \"Alert\"\n\nconst AlertTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h5\n    ref={ref}\n    className={cn(\"mb-1 font-medium leading-none tracking-tight\", className)}\n    {...props}\n  />\n))\nAlertTitle.displayName = \"AlertTitle\"\n\nconst AlertDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"text-sm [&_p]:leading-relaxed\", className)}\n    {...props}\n  />\n))\nAlertDescription.displayName = \"AlertDescription\"\n\nexport { Alert, AlertTitle, AlertDescription }\n"
  },
  {
    "path": "components/ui/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/button.tsx",
    "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-9 px-4 py-2\",\n        sm: \"h-8 rounded-md px-3 text-xs\",\n        lg: \"h-10 rounded-md px-8\",\n        icon: \"h-9 w-9\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\"\n    return (\n      <Comp\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
  },
  {
    "path": "components/ui/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-xl border bg-card text-card-foreground shadow\",\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(\"font-semibold leading-none tracking-tight\", className)}\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 { Cross2Icon } from \"@radix-ui/react-icons\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Dialog = DialogPrimitive.Root\n\nconst DialogTrigger = DialogPrimitive.Trigger\n\nconst DialogPortal = DialogPrimitive.Portal\n\nconst DialogClose = DialogPrimitive.Close\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-black/80  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\",\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        <Cross2Icon 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  DialogPortal,\n  DialogOverlay,\n  DialogTrigger,\n  DialogClose,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n}\n"
  },
  {
    "path": "components/ui/dropdown-menu.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport {\n  CheckIcon,\n  ChevronRightIcon,\n  DotFilledIcon,\n} from \"@radix-ui/react-icons\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n    inset?: boolean\n  }\n>(({ className, inset, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <ChevronRightIcon className=\"ml-auto h-4 w-4\" />\n  </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName =\n  DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuSubContent.displayName =\n  DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n  <DropdownMenuPrimitive.Portal>\n    <DropdownMenuPrimitive.Content\n      ref={ref}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md\",\n        \"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        className\n      )}\n      {...props}\n    />\n  </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n  <DropdownMenuPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <CheckIcon className=\"h-4 w-4\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName =\n  DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <DotFilledIcon className=\"h-4 w-4 fill-current\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Label\n    ref={ref}\n    className={cn(\n      \"px-2 py-1.5 text-sm font-semibold\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n    {...props}\n  />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)}\n      {...props}\n    />\n  )\n}\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\"\n\nexport {\n  DropdownMenu,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuCheckboxItem,\n  DropdownMenuRadioItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuGroup,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuRadioGroup,\n}\n"
  },
  {
    "path": "components/ui/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-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring 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/select.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  CaretSortIcon,\n  CheckIcon,\n  ChevronDownIcon,\n  ChevronUpIcon,\n} from \"@radix-ui/react-icons\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\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-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <SelectPrimitive.Icon asChild>\n      <CaretSortIcon className=\"h-4 w-4 opacity-50\" />\n    </SelectPrimitive.Icon>\n  </SelectPrimitive.Trigger>\n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n  <SelectPrimitive.ScrollUpButton\n    ref={ref}\n    className={cn(\n      \"flex cursor-default items-center justify-center py-1\",\n      className\n    )}\n    {...props}\n  >\n    <ChevronUpIcon />\n  </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n  React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n  React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n  <SelectPrimitive.ScrollDownButton\n    ref={ref}\n    className={cn(\n      \"flex cursor-default items-center justify-center py-1\",\n      className\n    )}\n    {...props}\n  >\n    <ChevronDownIcon />\n  </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName =\n  SelectPrimitive.ScrollDownButton.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 max-h-96 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      <SelectScrollUpButton />\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      <SelectScrollDownButton />\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(\"px-2 py-1.5 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-2 pr-8 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 right-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <SelectPrimitive.ItemIndicator>\n        <CheckIcon className=\"h-4 w-4\" />\n      </SelectPrimitive.ItemIndicator>\n    </span>\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  SelectScrollUpButton,\n  SelectScrollDownButton,\n}\n"
  },
  {
    "path": "components/ui/separator.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Separator = React.forwardRef<\n  React.ElementRef<typeof SeparatorPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n  (\n    { className, orientation = \"horizontal\", decorative = true, ...props },\n    ref\n  ) => (\n    <SeparatorPrimitive.Root\n      ref={ref}\n      decorative={decorative}\n      orientation={orientation}\n      className={cn(\n        \"shrink-0 bg-border\",\n        orientation === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",\n        className\n      )}\n      {...props}\n    />\n  )\n)\nSeparator.displayName = SeparatorPrimitive.Root.displayName\n\nexport { Separator }\n"
  },
  {
    "path": "components/ui/skeleton.tsx",
    "content": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n  return (\n    <div\n      className={cn(\"animate-pulse rounded-md bg-primary/10\", className)}\n      {...props}\n    />\n  )\n}\n\nexport { Skeleton }\n"
  },
  {
    "path": "components/ui/switch.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Switch = React.forwardRef<\n  React.ElementRef<typeof SwitchPrimitives.Root>,\n  React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>\n>(({ className, ...props }, ref) => (\n  <SwitchPrimitives.Root\n    className={cn(\n      \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  >\n    <SwitchPrimitives.Thumb\n      className={cn(\n        \"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0\"\n      )}\n    />\n  </SwitchPrimitives.Root>\n))\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n"
  },
  {
    "path": "components/ui/tabs.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Tabs = TabsPrimitive.Root\n\nconst TabsList = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.List\n    ref={ref}\n    className={cn(\n      \"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\nTabsList.displayName = TabsPrimitive.List.displayName\n\nconst TabsTrigger = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Trigger\n    ref={ref}\n    className={cn(\n      \"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow\",\n      className\n    )}\n    {...props}\n  />\n))\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName\n\nconst TabsContent = React.forwardRef<\n  React.ElementRef<typeof TabsPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <TabsPrimitive.Content\n    ref={ref}\n    className={cn(\n      \"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n      className\n    )}\n    {...props}\n  />\n))\nTabsContent.displayName = TabsPrimitive.Content.displayName\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent }\n"
  },
  {
    "path": "components/ui/textarea.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface TextareaProps\n  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n  ({ className, ...props }, ref) => {\n    return (\n      <textarea\n        className={cn(\n          \"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nTextarea.displayName = \"Textarea\"\n\nexport { Textarea }\n"
  },
  {
    "path": "components/ui/toast.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Cross2Icon } from \"@radix-ui/react-icons\"\nimport * as ToastPrimitives from \"@radix-ui/react-toast\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst ToastProvider = ToastPrimitives.Provider\n\nconst ToastViewport = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Viewport>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Viewport\n    ref={ref}\n    className={cn(\n      \"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]\",\n      className\n    )}\n    {...props}\n  />\n))\nToastViewport.displayName = ToastPrimitives.Viewport.displayName\n\nconst toastVariants = cva(\n  \"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full\",\n  {\n    variants: {\n      variant: {\n        default: \"border bg-background text-foreground\",\n        destructive:\n          \"destructive group border-destructive bg-destructive text-destructive-foreground\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst Toast = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Root>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &\n    VariantProps<typeof toastVariants>\n>(({ className, variant, ...props }, ref) => {\n  return (\n    <ToastPrimitives.Root\n      ref={ref}\n      className={cn(toastVariants({ variant }), className)}\n      {...props}\n    />\n  )\n})\nToast.displayName = ToastPrimitives.Root.displayName\n\nconst ToastAction = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Action>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Action\n    ref={ref}\n    className={cn(\n      \"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive\",\n      className\n    )}\n    {...props}\n  />\n))\nToastAction.displayName = ToastPrimitives.Action.displayName\n\nconst ToastClose = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Close>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Close\n    ref={ref}\n    className={cn(\n      \"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600\",\n      className\n    )}\n    toast-close=\"\"\n    {...props}\n  >\n    <Cross2Icon className=\"h-4 w-4\" />\n  </ToastPrimitives.Close>\n))\nToastClose.displayName = ToastPrimitives.Close.displayName\n\nconst ToastTitle = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Title>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Title\n    ref={ref}\n    className={cn(\"text-sm font-semibold [&+div]:text-xs\", className)}\n    {...props}\n  />\n))\nToastTitle.displayName = ToastPrimitives.Title.displayName\n\nconst ToastDescription = React.forwardRef<\n  React.ElementRef<typeof ToastPrimitives.Description>,\n  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>\n>(({ className, ...props }, ref) => (\n  <ToastPrimitives.Description\n    ref={ref}\n    className={cn(\"text-sm opacity-90\", className)}\n    {...props}\n  />\n))\nToastDescription.displayName = ToastPrimitives.Description.displayName\n\ntype ToastProps = React.ComponentPropsWithoutRef<typeof Toast>\n\ntype ToastActionElement = React.ReactElement<typeof ToastAction>\n\nexport {\n  type ToastProps,\n  type ToastActionElement,\n  ToastProvider,\n  ToastViewport,\n  Toast,\n  ToastTitle,\n  ToastDescription,\n  ToastClose,\n  ToastAction,\n}\n"
  },
  {
    "path": "components/ui/toaster.tsx",
    "content": "\"use client\"\n\nimport { useToast } from \"@/hooks/use-toast\"\nimport {\n  Toast,\n  ToastClose,\n  ToastDescription,\n  ToastProvider,\n  ToastTitle,\n  ToastViewport,\n} from \"@/components/ui/toast\"\n\nexport function Toaster() {\n  const { toasts } = useToast()\n\n  return (\n    <ToastProvider>\n      {toasts.map(function ({ id, title, description, action, ...props }) {\n        return (\n          <Toast key={id} {...props}>\n            <div className=\"grid gap-1\">\n              {title && <ToastTitle>{title}</ToastTitle>}\n              {description && (\n                <ToastDescription>{description}</ToastDescription>\n              )}\n            </div>\n            {action}\n            <ToastClose />\n          </Toast>\n        )\n      })}\n      <ToastViewport />\n    </ToastProvider>\n  )\n}\n"
  },
  {
    "path": "components/user.tsx",
    "content": "import { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { Session } from '@supabase/supabase-js'\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Button } from \"./ui/button\";\nimport { LogOut } from \"lucide-react\";\n\ninterface Props {\n  session: Session | null,\n  signOut: () => void,\n  showLogin: () => void,\n}\n\nexport function User({ session, signOut, showLogin }: Props) {\n  return (\n    <>\n      {session && (\n        <DropdownMenu>\n        <DropdownMenuTrigger asChild>\n          <Avatar className=\"cursor-pointer\">\n            <AvatarImage src={session.user?.user_metadata?.avatar_url}/>\n            <AvatarFallback>{session.user.user_metadata.name}</AvatarFallback>\n          </Avatar>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent className=\"mx-4\">\n          <DropdownMenuLabel className=\"text-center truncate\">\n            {session.user.user_metadata.name}\n          </DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          <DropdownMenuLabel className=\"text-center truncate\">\n            <Button variant=\"ghost\" size=\"icon\" className=\"w-full h-full text-sm font-medium justify-start hover:text-gray-400\" onClick={signOut}>\n              Sign Out\n            </Button>\n          </DropdownMenuLabel>\n        </DropdownMenuContent>\n      </DropdownMenu>\n      )}\n\n      {!session && (\n        <Button variant=\"secondary\" size=\"icon\" className=\"text-sm font-medium px-8 py-2\" onClick={showLogin}>\n          Sign in\n        </Button>\n      )}\n    </>\n    \n  );\n}\n"
  },
  {
    "path": "components/welcome.tsx",
    "content": "'use client'\n\nimport React, { FormEvent, ChangeEvent, useEffect } from 'react';\nimport { Sparkles, Presentation } from 'lucide-react';\nimport { Button } from '@/components/ui/button';\nimport { Textarea } from './ui/textarea';\n\nconst Lint = [\n  \"Create a PPT on how ChatGPT works.\",\n  \"Generate a PPT explaining the API integration of ChatGPT in web applications, including key steps and practical examples.\",\n  \"Compares ChatGPT with other conversational AI models, such as Gemini, in terms of architecture and performance.\"\n]\n\ninterface Props {\n  onSubmit: (e?: FormEvent<HTMLFormElement>) => void\n  onChange: (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void\n  setChatInput: (input: string) => void\n  value: string\n}\nexport default function Welcome({onSubmit, onChange, value, setChatInput}: Props) {\n  const handleClick = (lint: string) => {\n    setChatInput(lint)\n  }\n\n  const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n    if (event.key === 'Enter' && !event.shiftKey) {\n      event.preventDefault();\n      onSubmit();\n    }\n  };\n\n  return (\n    <div className=\"max-w-2xl mx-auto p-8 rounded-xl shadow-lg transition-all duration-300 hover:shadow-xl mt-[150px]\">\n      <h1 className=\"text-2xl font-serif mb-8 flex items-center text-gray-100 animate-fade-in\">\n        <Presentation className=\"w-10 h-10 text-[#c5f955] mr-3 animate-pulse\" />\n        <div>\n          <p>\n              <span>\n                Type the Topic for your Presentation, \n              </span>\n              <span  className='relative'>\n                It is Free!\n                <svg\n                  aria-hidden=\"true\"\n                  viewBox=\"0 0 418 42\"\n                  className=\"absolute left-0 top-2/3 h-[0.58em] w-full fill-blue-300/70\"\n                  preserveAspectRatio=\"none\"\n                >\n                  <path d=\"M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z\"></path>\n                </svg>\n              </span>\n              \n          </p>\n        </div>\n      </h1>\n      <div>\n        <form className=\"relative\" onSubmit={onSubmit}>\n          <Textarea\n            className=\"w-full p-[20px] pr-[50px] text-white bg-black border rounded-lg resize-none focus-visible:outline-none\"\n            rows={7}\n            value={value}\n            onKeyDown={handleKeyDown}\n            onChange={onChange}\n            placeholder=\"Describe your topic...\"\n          />\n          {value && \n          <Button variant=\"secondary\" size=\"icon\" className='absolute right-3 top-3 text-white p-2 rounded-full'>\n            <Sparkles className=\"h-5 w-5 text-[#c5f955]\" />\n          </Button>}\n          <div className=\"absolute left-3 bottom-3 right-3 flex justify-between items-center text-sm text-gray-600\">\n            <span>Gemini Flash</span>\n          </div>\n        </form>\n      </div>\n      <div className=\"mt-6\">\n        <p className=\"text-sm text-gray-400 mb-3 font-medium\">Get started with an example:</p>\n        <div className=\"flex flex-wrap gap-2\">\n          {Lint.map((example, index) => (\n            <button \n              key={index} \n              className=\"px-4 py-2 bg-black rounded-full text-sm text-gray-500 hover:bg-gray-800 hover:text-gray-300 transition-all duration-300 hover:shadow-md text-left\"\n              onClick={() => handleClick(example)}\n            >\n              {example}\n            </button>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n};\n\n"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"new-york\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.ts\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"neutral\",\n    \"cssVariables\": true,\n    \"prefix\": \"\"\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\",\n    \"ui\": \"@/components/ui\",\n    \"lib\": \"@/lib\",\n    \"hooks\": \"@/hooks\"\n  }\n}"
  },
  {
    "path": "debug/apitest.http",
    "content": "@baseUri = http://127.0.0.1:3000/api \n\nGET {{baseUri}}/limit\nContent-Type: application/json\n\n{}\n\nPOST {{baseUri}}/sandbox\nContent-Type: application/json\n\n{\n    \"artifact\": {\n      \"code\": \"#title\"\n    }\n}\n\nPOST {{baseUri}}/chat\nContent-Type: application/json\n\n{\n    \"messages\": [{\n      \"role\": \"user\",\n      \"content\": [{ \"type\": \"text\", \"text\": \"introduce slidev\" }]\n    }]\n}\n\nPOST {{baseUri}}/convertd\n\n{\n    \"artifact\": {\n     \"code\": \"#title\"\n    }\n}\n\n"
  },
  {
    "path": "hooks/use-toast.ts",
    "content": "\"use client\"\n\n// Inspired by react-hot-toast library\nimport * as React from \"react\"\n\nimport type {\n  ToastActionElement,\n  ToastProps,\n} from \"@/components/ui/toast\"\n\nconst TOAST_LIMIT = 1\nconst TOAST_REMOVE_DELAY = 1000000\n\ntype ToasterToast = ToastProps & {\n  id: string\n  title?: React.ReactNode\n  description?: React.ReactNode\n  action?: ToastActionElement\n}\n\nconst actionTypes = {\n  ADD_TOAST: \"ADD_TOAST\",\n  UPDATE_TOAST: \"UPDATE_TOAST\",\n  DISMISS_TOAST: \"DISMISS_TOAST\",\n  REMOVE_TOAST: \"REMOVE_TOAST\",\n} as const\n\nlet count = 0\n\nfunction genId() {\n  count = (count + 1) % Number.MAX_SAFE_INTEGER\n  return count.toString()\n}\n\ntype ActionType = typeof actionTypes\n\ntype Action =\n  | {\n      type: ActionType[\"ADD_TOAST\"]\n      toast: ToasterToast\n    }\n  | {\n      type: ActionType[\"UPDATE_TOAST\"]\n      toast: Partial<ToasterToast>\n    }\n  | {\n      type: ActionType[\"DISMISS_TOAST\"]\n      toastId?: ToasterToast[\"id\"]\n    }\n  | {\n      type: ActionType[\"REMOVE_TOAST\"]\n      toastId?: ToasterToast[\"id\"]\n    }\n\ninterface State {\n  toasts: ToasterToast[]\n}\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n\nconst addToRemoveQueue = (toastId: string) => {\n  if (toastTimeouts.has(toastId)) {\n    return\n  }\n\n  const timeout = setTimeout(() => {\n    toastTimeouts.delete(toastId)\n    dispatch({\n      type: \"REMOVE_TOAST\",\n      toastId: toastId,\n    })\n  }, TOAST_REMOVE_DELAY)\n\n  toastTimeouts.set(toastId, timeout)\n}\n\nexport const reducer = (state: State, action: Action): State => {\n  switch (action.type) {\n    case \"ADD_TOAST\":\n      return {\n        ...state,\n        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),\n      }\n\n    case \"UPDATE_TOAST\":\n      return {\n        ...state,\n        toasts: state.toasts.map((t) =>\n          t.id === action.toast.id ? { ...t, ...action.toast } : t\n        ),\n      }\n\n    case \"DISMISS_TOAST\": {\n      const { toastId } = action\n\n      // ! Side effects ! - This could be extracted into a dismissToast() action,\n      // but I'll keep it here for simplicity\n      if (toastId) {\n        addToRemoveQueue(toastId)\n      } else {\n        state.toasts.forEach((toast) => {\n          addToRemoveQueue(toast.id)\n        })\n      }\n\n      return {\n        ...state,\n        toasts: state.toasts.map((t) =>\n          t.id === toastId || toastId === undefined\n            ? {\n                ...t,\n                open: false,\n              }\n            : t\n        ),\n      }\n    }\n    case \"REMOVE_TOAST\":\n      if (action.toastId === undefined) {\n        return {\n          ...state,\n          toasts: [],\n        }\n      }\n      return {\n        ...state,\n        toasts: state.toasts.filter((t) => t.id !== action.toastId),\n      }\n  }\n}\n\nconst listeners: Array<(state: State) => void> = []\n\nlet memoryState: State = { toasts: [] }\n\nfunction dispatch(action: Action) {\n  memoryState = reducer(memoryState, action)\n  listeners.forEach((listener) => {\n    listener(memoryState)\n  })\n}\n\ntype Toast = Omit<ToasterToast, \"id\">\n\nfunction toast({ ...props }: Toast) {\n  const id = genId()\n\n  const update = (props: ToasterToast) =>\n    dispatch({\n      type: \"UPDATE_TOAST\",\n      toast: { ...props, id },\n    })\n  const dismiss = () => dispatch({ type: \"DISMISS_TOAST\", toastId: id })\n\n  dispatch({\n    type: \"ADD_TOAST\",\n    toast: {\n      ...props,\n      id,\n      open: true,\n      onOpenChange: (open) => {\n        if (!open) dismiss()\n      },\n    },\n  })\n\n  return {\n    id: id,\n    dismiss,\n    update,\n  }\n}\n\nfunction useToast() {\n  const [state, setState] = React.useState<State>(memoryState)\n\n  React.useEffect(() => {\n    listeners.push(setState)\n    return () => {\n      const index = listeners.indexOf(setState)\n      if (index > -1) {\n        listeners.splice(index, 1)\n      }\n    }\n  }, [state])\n\n  return {\n    ...state,\n    toast,\n    dismiss: (toastId?: string) => dispatch({ type: \"DISMISS_TOAST\", toastId }),\n  }\n}\n\nexport { useToast, toast }\n"
  },
  {
    "path": "lib/auth.ts",
    "content": "import { useState, useEffect } from 'react'\nimport { Session } from '@supabase/supabase-js'\nimport { supabase } from './supabase'\nimport { usePostHog } from 'posthog-js/react'\n\ninterface UserTeam {\n  id: string;\n  name: string;\n  is_default: boolean;\n  tier: string;\n  email: string;\n  team_api_keys: { api_key: string; }[];\n}\n\nexport type AuthViewType = \"sign_in\" | \"sign_up\" | \"magic_link\" | \"forgotten_password\" | \"update_password\" | \"verify_otp\"\n\nexport async function getUserAPIKey (session: Session) {\n  const { data: userTeams } = await supabase\n    .from('users_teams')\n    .select('teams (id, name, is_default, tier, email, team_api_keys (api_key))')\n    .eq('user_id', session?.user.id)\n\n  const teams = userTeams?.map((userTeam: any) => userTeam.teams).map((team: UserTeam) => {\n    return {\n      ...team,\n      apiKeys: team.team_api_keys.map(apiKey => apiKey.api_key)\n    }\n  })\n\n  const defaultTeam = teams?.find(team => team.is_default)\n  return defaultTeam?.apiKeys[0]\n}\n\n\nexport function useAuth (setAuthDialog: (value: boolean) => void, setAuthView: (value: AuthViewType) => void) {\n  const [session, setSession] = useState<Session | null>(null)\n  const [apiKey, setApiKey] = useState<string | undefined>(undefined)\n  const posthog = usePostHog()\n  let recovery = false\n\n  useEffect(() => {\n    supabase.auth.getSession().then(({ data: { session } }) => {\n      getUserAPIKey(session as Session).then(setApiKey)\n      setSession(session)\n    })\n\n    const {\n      data: { subscription },\n    } = supabase.auth.onAuthStateChange((_event, session) => {\n      setSession(session)\n\n      if (_event === 'PASSWORD_RECOVERY') {\n        recovery = true\n        setAuthView('update_password')\n        setAuthDialog(true)\n      }\n\n      if (_event === 'USER_UPDATED' && recovery) {\n        recovery = false\n      }\n\n      if (_event === 'SIGNED_IN' && !recovery) {\n        setAuthDialog(false)\n        getUserAPIKey(session as Session).then(setApiKey)\n        posthog.identify(session?.user.id, { email: session?.user.email })\n        posthog.capture('sign_in')\n      }\n\n      if (_event === 'SIGNED_OUT') {\n        setApiKey(undefined)\n        setAuthView('sign_in')\n        posthog.capture('sign_out')\n        posthog.reset()\n      }\n    })\n\n    return () => subscription.unsubscribe()\n  }, [])\n\n  return {\n    session,\n    apiKey\n  }\n}\n"
  },
  {
    "path": "lib/messages.ts",
    "content": "export type MessageText = {\n  type: 'text'\n  text: string\n}\n\nexport type MessageCode = {\n  type: 'code'\n  text: string\n}\n\nexport type MessageImage = {\n  type: 'image'\n  image: string\n}\n\nexport type ChatMessage = {\n  role: 'assistant' | 'user'\n  content: Array<MessageText | MessageCode | MessageImage>\n  meta?: {\n    title?: string\n    description?: string\n  }\n}\n\nexport function toAISDKMessages(messages: ChatMessage[]) {\n  return messages.map(message => ({\n    role: message.role,\n    content: message.content.map(content => {\n      if (content.type === 'code') {\n        return {\n          type: 'text',\n          text: content.text\n        }\n      }\n\n      return content\n    })\n  }))\n}\n\nexport async function toMessageImage(files: FileList | null) {\n  if (!files || files.length === 0) {\n    return []\n  }\n\n  return Promise.all(Array.from(files).map(async file => {\n    const base64 = Buffer.from(await file.arrayBuffer()).toString('base64')\n    return `data:${file.type};base64,${base64}`\n  }))\n}\n"
  },
  {
    "path": "lib/ratelimit.ts",
    "content": "import { kv } from '@vercel/kv'\nimport { Ratelimit } from '@upstash/ratelimit'\n\nexport type Unit = \"ms\" | \"s\" | \"m\" | \"h\" | \"d\"\nexport type Duration = `${number} ${Unit}` | `${number}${Unit}`\n\nexport default async function ratelimit (key: string | null, maxRequests: number, window: Duration) {\n  if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) {\n    const ratelimit = new Ratelimit({\n      redis: kv,\n      limiter: Ratelimit.slidingWindow(maxRequests, window)\n    })\n\n    const { success, limit, reset, remaining } = await ratelimit.limit(\n      `ratelimit_${key}`\n    )\n\n    return {\n      amount: limit,\n      reset,\n      remaining,\n      success\n    }\n  }\n}\n"
  },
  {
    "path": "lib/schema.ts",
    "content": "import { z } from 'zod'\n\nexport const artifactSchema = z.object({\n  commentary: z.string().describe(`Describe what you're about to do and the steps you want to take for generating the code in great detail.`),\n  title: z.string().describe('Short title of the code. Max 3 words.'),\n  description: z.string().describe('Short description of the code. Max 1 sentence.'),\n  code: z.string().describe('code generated. Only runnable code is allowed.'),\n})\n\nexport type ArtifactSchema = z.infer<typeof artifactSchema>\n"
  },
  {
    "path": "lib/supabase.ts",
    "content": "import { createClient } from '@supabase/supabase-js'\n\nexport const supabase = createClient(\n  process.env.NEXT_PUBLIC_SUPABASE_URL!,\n  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!\n)\n\n"
  },
  {
    "path": "lib/template.ts",
    "content": "export const htmlTemplate = `\n  <!doctype html>\n  <html>\n    <head>\n      <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.3.1/reveal.min.css\">\n      <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.3.1/theme/black.min.css\">\n    </head>\n    <body>\n      <div class=\"reveal\">\n        <div class=\"slides\">\n          // generate slides here\n        </div>\n      </div>\n      <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.3.1/reveal.min.js\"></script>\n      <script>\n        Reveal.initialize();\n      </script>\n    </body>\n  </html>\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"
  },
  {
    "path": "next.config.mjs",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nexport default nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"revealjs-ai\",\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    \"@ai-sdk/anthropic\": \"^0.0.50\",\n    \"@ai-sdk/google\": \"^0.0.51\",\n    \"@e2b/code-interpreter\": \"0.0.9-beta.3\",\n    \"@radix-ui/react-avatar\": \"^1.1.0\",\n    \"@radix-ui/react-dialog\": \"^1.1.1\",\n    \"@radix-ui/react-dropdown-menu\": \"^2.1.1\",\n    \"@radix-ui/react-icons\": \"^1.3.0\",\n    \"@radix-ui/react-label\": \"^2.1.0\",\n    \"@radix-ui/react-select\": \"^2.1.1\",\n    \"@radix-ui/react-separator\": \"^1.1.0\",\n    \"@radix-ui/react-slot\": \"^1.1.0\",\n    \"@radix-ui/react-switch\": \"^1.1.0\",\n    \"@radix-ui/react-tabs\": \"^1.1.0\",\n    \"@radix-ui/react-toast\": \"^1.2.1\",\n    \"@stripe/stripe-js\": \"^4.5.0\",\n    \"@supabase/auth-ui-react\": \"^0.4.7\",\n    \"@supabase/auth-ui-shared\": \"^0.1.8\",\n    \"@supabase/supabase-js\": \"^2.45.4\",\n    \"@upstash/ratelimit\": \"^2.0.3\",\n    \"@vercel/kv\": \"^2.0.0\",\n    \"ai\": \"^3.3.42\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.1.1\",\n    \"lucide-react\": \"^0.441.0\",\n    \"next\": \"14.2.12\",\n    \"posthog-js\": \"^1.161.6\",\n    \"react\": \"^18\",\n    \"react-dom\": \"^18\",\n    \"react-icons\": \"^5.3.0\",\n    \"react-share\": \"^5.1.0\",\n    \"react-syntax-highlighter\": \"^15.5.0\",\n    \"react-toastify\": \"^10.0.5\",\n    \"stripe\": \"^16.12.0\",\n    \"tailwind-merge\": \"^2.5.2\",\n    \"tailwindcss-animate\": \"^1.0.7\",\n    \"usehooks-ts\": \"^3.1.0\",\n    \"zod\": \"^3.23.8\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20\",\n    \"@types/react\": \"^18\",\n    \"@types/react-dom\": \"^18\",\n    \"@types/react-syntax-highlighter\": \"^15.5.13\",\n    \"eslint\": \"^8\",\n    \"eslint-config-next\": \"14.2.12\",\n    \"postcss\": \"^8\",\n    \"tailwindcss\": \"^3.4.1\",\n    \"typescript\": \"^5\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.mjs",
    "content": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n  plugins: {\n    tailwindcss: {},\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "sandbox-templates/e2b.Dockerfile",
    "content": "# You can use most Debian-based base images\nFROM node:21-slim\n\n# Install dependencies and customize sandbox\nWORKDIR /home/user/slidev\nRun npm install -g npm\nRUN npm install @slidev/cli @slidev/theme-default @slidev/theme-seriph\n\nRUN touch slides.md\nRUN apt-get update && apt-get install -y xdg-utils && apt-get clean && rm -rf /var/lib/apt/lists/*\nRUN mv /home/user/slidev/* /home/user/ && rm -rf /home/user/slidev\n\nEXPOSE 3030\n# Move the Vue app to the home directory and remove the Vue directory\nCMD [\"npx\", \"slidev\", \"--remote\"]\n"
  },
  {
    "path": "sandbox-templates/e2b.toml",
    "content": "# This is a config for E2B sandbox template.\n# You can use 'template_id' (toyw6mhmdw42n4wyhdcb) or 'template_name (my-slidev-developer) from this config to spawn a sandbox:\n\n# Python SDK\n# from e2b import Sandbox\n# sandbox = Sandbox(template='my-slidev-developer')\n\n# JS SDK\n# import { Sandbox } from 'e2b'\n# const sandbox = await Sandbox.create({ template: 'my-slidev-developer' })\n\nteam_id = \"df0c5b73-b47c-431e-a90c-9a32db676fb3\"\nstart_cmd = \"cd /home/user && npx slidev\"\ndockerfile = \"e2b.Dockerfile\"\ntemplate_name = \"my-slidev-developer\"\ntemplate_id = \"toyw6mhmdw42n4wyhdcb\"\n"
  },
  {
    "path": "tailwind.config.ts",
    "content": "import type { Config } from \"tailwindcss\";\n\nconst config: Config = {\n    darkMode: [\"class\"],\n    content: [\n    \"./pages/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./components/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./app/**/*.{js,ts,jsx,tsx,mdx}\",\n  ],\n  theme: {\n  \textend: {\n  \t\tcolors: {\n  \t\t\tbackground: 'hsl(var(--background))',\n  \t\t\tforeground: 'hsl(var(--foreground))',\n  \t\t\tcard: {\n  \t\t\t\tDEFAULT: 'hsl(var(--card))',\n  \t\t\t\tforeground: 'hsl(var(--card-foreground))'\n  \t\t\t},\n  \t\t\tpopover: {\n  \t\t\t\tDEFAULT: 'hsl(var(--popover))',\n  \t\t\t\tforeground: 'hsl(var(--popover-foreground))'\n  \t\t\t},\n  \t\t\tprimary: {\n  \t\t\t\tDEFAULT: 'hsl(var(--primary))',\n  \t\t\t\tforeground: 'hsl(var(--primary-foreground))'\n  \t\t\t},\n  \t\t\tsecondary: {\n  \t\t\t\tDEFAULT: 'hsl(var(--secondary))',\n  \t\t\t\tforeground: 'hsl(var(--secondary-foreground))'\n  \t\t\t},\n  \t\t\tmuted: {\n  \t\t\t\tDEFAULT: 'hsl(var(--muted))',\n  \t\t\t\tforeground: 'hsl(var(--muted-foreground))'\n  \t\t\t},\n  \t\t\taccent: {\n  \t\t\t\tDEFAULT: 'hsl(var(--accent))',\n  \t\t\t\tforeground: 'hsl(var(--accent-foreground))'\n  \t\t\t},\n  \t\t\tdestructive: {\n  \t\t\t\tDEFAULT: 'hsl(var(--destructive))',\n  \t\t\t\tforeground: 'hsl(var(--destructive-foreground))'\n  \t\t\t},\n  \t\t\tborder: 'hsl(var(--border))',\n  \t\t\tinput: 'hsl(var(--input))',\n  \t\t\tring: 'hsl(var(--ring))',\n  \t\t\tchart: {\n  \t\t\t\t'1': 'hsl(var(--chart-1))',\n  \t\t\t\t'2': 'hsl(var(--chart-2))',\n  \t\t\t\t'3': 'hsl(var(--chart-3))',\n  \t\t\t\t'4': 'hsl(var(--chart-4))',\n  \t\t\t\t'5': 'hsl(var(--chart-5))'\n  \t\t\t}\n  \t\t},\n  \t\tborderRadius: {\n  \t\t\tlg: 'var(--radius)',\n  \t\t\tmd: 'calc(var(--radius) - 2px)',\n  \t\t\tsm: 'calc(var(--radius) - 4px)'\n  \t\t}\n  \t}\n  },\n  plugins: [require(\"tailwindcss-animate\")],\n};\nexport default config;\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": 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"
  }
]