[
  {
    "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.env\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"
  },
  {
    "path": "README.md",
    "content": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.ts\",\n    \"css\": \"src/app/globals.css\",\n    \"baseColor\": \"slate\",\n    \"cssVariables\": true\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\"\n  }\n}"
  },
  {
    "path": "drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\";\nimport * as dotenv from \"dotenv\";\n\ndotenv.config({\n  path: \".env\",\n});\n\nexport default {\n  driver: \"pg\",\n  schema: \"./src/lib/db/schema.ts\",\n  dbCredentials: {\n    connectionString: process.env.DATABASE_URL!,\n  },\n} satisfies Config;\n"
  },
  {
    "path": "next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  images: {\n    domains: [\"firebasestorage.googleapis.com\"],\n  },\n  typescript: {\n    ignoreBuildErrors: true,\n  },\n  eslint: {\n    ignoreDuringBuilds: true,\n  },\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"aideation-yt\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@clerk/backend\": \"^0.29.0\",\n    \"@clerk/nextjs\": \"^4.24.1\",\n    \"@neondatabase/serverless\": \"^0.6.0\",\n    \"@radix-ui/react-dialog\": \"^1.0.4\",\n    \"@radix-ui/react-separator\": \"^1.0.3\",\n    \"@radix-ui/react-slot\": \"^1.0.2\",\n    \"@tanstack/react-query\": \"^4.35.3\",\n    \"@tiptap/pm\": \"^2.1.11\",\n    \"@tiptap/react\": \"^2.1.11\",\n    \"@tiptap/starter-kit\": \"^2.1.11\",\n    \"@types/node\": \"20.6.4\",\n    \"@types/react\": \"18.2.22\",\n    \"@types/react-dom\": \"18.2.7\",\n    \"ai\": \"^2.2.13\",\n    \"autoprefixer\": \"10.4.16\",\n    \"axios\": \"^1.5.0\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.0.0\",\n    \"dotenv\": \"^16.3.1\",\n    \"drizzle-kit\": \"^0.19.13\",\n    \"drizzle-orm\": \"^0.28.6\",\n    \"encoding\": \"^0.1.13\",\n    \"eslint\": \"8.50.0\",\n    \"eslint-config-next\": \"13.5.2\",\n    \"firebase\": \"^10.4.0\",\n    \"lucide-react\": \"^0.279.0\",\n    \"next\": \"13.5.2\",\n    \"openai-edge\": \"^1.2.2\",\n    \"pg\": \"^8.11.3\",\n    \"postcss\": \"8.4.30\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"tailwind-merge\": \"^1.14.0\",\n    \"tailwindcss\": \"3.3.3\",\n    \"tailwindcss-animate\": \"^1.0.7\",\n    \"typescript\": \"5.2.2\",\n    \"typewriter-effect\": \"^2.21.0\"\n  },\n  \"devDependencies\": {\n    \"@tailwindcss/typography\": \"^0.5.10\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "src/app/api/completion/route.ts",
    "content": "import { OpenAIApi, Configuration } from \"openai-edge\";\nimport { OpenAIStream, StreamingTextResponse } from \"ai\";\n// /api/completion\nconst config = new Configuration({\n  apiKey: process.env.OPENAI_API_KEY,\n});\n\nconst openai = new OpenAIApi(config);\n\nexport async function POST(req: Request) {\n  // extract the prompt from the body\n  const { prompt } = await req.json();\n\n  const response = await openai.createChatCompletion({\n    model: \"gpt-3.5-turbo\",\n    messages: [\n      {\n        role: \"system\",\n        content: `You are a helpful AI embedded in a notion text editor app that is used to autocomplete sentences\n            The traits of AI include expert knowledge, helpfulness, cleverness, and articulateness.\n        AI is a well-behaved and well-mannered individual.\n        AI is always friendly, kind, and inspiring, and he is eager to provide vivid and thoughtful responses to the user.`,\n      },\n      {\n        role: \"user\",\n        content: `\n        I am writing a piece of text in a notion text editor app.\n        Help me complete my train of thought here: ##${prompt}##\n        keep the tone of the text consistent with the rest of the text.\n        keep the response short and sweet.\n        `,\n      },\n    ],\n    stream: true,\n  });\n  const stream = OpenAIStream(response);\n  return new StreamingTextResponse(stream);\n}\n"
  },
  {
    "path": "src/app/api/createNoteBook/route.ts",
    "content": "// /api/createNoteBook\n\nimport { db } from \"@/lib/db\";\nimport { $notes } from \"@/lib/db/schema\";\nimport { generateImage, generateImagePrompt } from \"@/lib/openai\";\nimport { auth } from \"@clerk/nextjs\";\nimport { NextResponse } from \"next/server\";\n\nexport const runtime = \"edge\";\n\nexport async function POST(req: Request) {\n  const { userId } = auth();\n  if (!userId) {\n    return new NextResponse(\"unauthorised\", { status: 401 });\n  }\n  const body = await req.json();\n  const { name } = body;\n  const image_description = await generateImagePrompt(name);\n  if (!image_description) {\n    return new NextResponse(\"failed to generate image description\", {\n      status: 500,\n    });\n  }\n  const image_url = await generateImage(image_description);\n  if (!image_url) {\n    return new NextResponse(\"failed to generate image \", {\n      status: 500,\n    });\n  }\n\n  const note_ids = await db\n    .insert($notes)\n    .values({\n      name,\n      userId,\n      imageUrl: image_url,\n    })\n    .returning({\n      insertedId: $notes.id,\n    });\n\n  return NextResponse.json({\n    note_id: note_ids[0].insertedId,\n  });\n}\n"
  },
  {
    "path": "src/app/api/deleteNote/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { $notes } from \"@/lib/db/schema\";\nimport { eq } from \"drizzle-orm\";\nimport { NextResponse } from \"next/server\";\n\nexport async function POST(req: Request) {\n  const { noteId } = await req.json();\n  await db.delete($notes).where(eq($notes.id, parseInt(noteId)));\n  return new NextResponse(\"ok\", { status: 200 });\n}\n"
  },
  {
    "path": "src/app/api/saveNote/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { $notes } from \"@/lib/db/schema\";\nimport { eq } from \"drizzle-orm\";\nimport { NextResponse } from \"next/server\";\n\nexport async function POST(req: Request) {\n  try {\n    const body = await req.json();\n    let { noteId, editorState } = body;\n    if (!editorState || !noteId) {\n      return new NextResponse(\"Missing editorState or noteId\", { status: 400 });\n    }\n\n    noteId = parseInt(noteId);\n    const notes = await db.select().from($notes).where(eq($notes.id, noteId));\n    if (notes.length != 1) {\n      return new NextResponse(\"failed to update\", { status: 500 });\n    }\n\n    const note = notes[0];\n    if (note.editorState !== editorState) {\n      await db\n        .update($notes)\n        .set({\n          editorState,\n        })\n        .where(eq($notes.id, noteId));\n    }\n    return NextResponse.json(\n      {\n        success: true,\n      },\n      { status: 200 }\n    );\n  } catch (error) {\n    console.error(error);\n    return NextResponse.json(\n      {\n        success: false,\n      },\n      { status: 500 }\n    );\n  }\n}\n"
  },
  {
    "path": "src/app/api/uploadToFirebase/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { $notes } from \"@/lib/db/schema\";\nimport { uploadFileToFirebase } from \"@/lib/firebase\";\nimport { eq } from \"drizzle-orm\";\nimport { NextResponse } from \"next/server\";\n\nexport async function POST(req: Request) {\n  try {\n    const { noteId } = await req.json();\n    // extract out the dalle imageurl\n    // save it to firebase\n    const notes = await db\n      .select()\n      .from($notes)\n      .where(eq($notes.id, parseInt(noteId)));\n    if (!notes[0].imageUrl) {\n      return new NextResponse(\"no image url\", { status: 400 });\n    }\n    const firebase_url = await uploadFileToFirebase(\n      notes[0].imageUrl,\n      notes[0].name\n    );\n    // update the note with the firebase url\n    await db\n      .update($notes)\n      .set({\n        imageUrl: firebase_url,\n      })\n      .where(eq($notes.id, parseInt(noteId)));\n    return new NextResponse(\"ok\", { status: 200 });\n  } catch (error) {\n    console.error(error);\n    return new NextResponse(\"error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "src/app/dashboard/page.tsx",
    "content": "import CreateNoteDialog from \"@/components/CreateNoteDialog\";\nimport { Button } from \"@/components/ui/button\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { db } from \"@/lib/db\";\nimport { $notes } from \"@/lib/db/schema\";\nimport { UserButton, auth } from \"@clerk/nextjs\";\nimport { eq } from \"drizzle-orm\";\nimport { ArrowLeft } from \"lucide-react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport React from \"react\";\n\ntype Props = {};\n\nconst DashboardPage = async (props: Props) => {\n  const { userId } = auth();\n  const notes = await db\n    .select()\n    .from($notes)\n    .where(eq($notes.userId, userId!));\n\n  return (\n    <>\n      <div className=\"grainy min-h-screen\">\n        <div className=\"max-w-7xl mx-auto p-10\">\n          <div className=\"h-14\"></div>\n          <div className=\"flex justify-between items-center md:flex-row flex-col\">\n            <div className=\"flex items-center\">\n              <Link href=\"/\">\n                <Button className=\"bg-green-600\" size=\"sm\">\n                  <ArrowLeft className=\"mr-1 w-4 h-4\" />\n                  Back\n                </Button>\n              </Link>\n              <div className=\"w-4\"></div>\n              <h1 className=\"text-3xl font-bold text-gray-900\">My Notes</h1>\n              <div className=\"w-4\"></div>\n              <UserButton />\n            </div>\n          </div>\n\n          <div className=\"h-8\"></div>\n          <Separator />\n          <div className=\"h-8\"></div>\n          {/* list all the notes */}\n          {/* if no notes, display this */}\n          {notes.length === 0 && (\n            <div className=\"text-center\">\n              <h2 className=\"text-xl text-gray-500\">You have no notes yet.</h2>\n            </div>\n          )}\n\n          {/* display all the notes */}\n          <div className=\"grid sm:grid-cols-3 md:grid-cols-5 grid-cols-1 gap-3\">\n            <CreateNoteDialog />\n            {notes.map((note) => {\n              return (\n                <a href={`/notebook/${note.id}`} key={note.id}>\n                  <div className=\"border border-stone-300 rounded-lg overflow-hidden flex flex-col hover:shadow-xl transition hover:-translate-y-1\">\n                    <Image\n                      width={400}\n                      height={200}\n                      alt={note.name}\n                      src={note.imageUrl || \"\"}\n                    />\n                    <div className=\"p-4\">\n                      <h3 className=\"text-xl font-semibold text-gray-900\">\n                        {note.name}\n                      </h3>\n                      <div className=\"h-1\"></div>\n                      <p className=\"text-sm text-gray-500\">\n                        {new Date(note.createdAt).toLocaleDateString()}\n                      </p>\n                    </div>\n                  </div>\n                </a>\n              );\n            })}\n          </div>\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default DashboardPage;\n"
  },
  {
    "path": "src/app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 84% 4.9%;\n\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n    --ring: 222.2 84% 4.9%;\n\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n\n    --card: 222.2 84% 4.9%;\n    --card-foreground: 210 40% 98%;\n\n    --popover: 222.2 84% 4.9%;\n    --popover-foreground: 210 40% 98%;\n\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 11.2%;\n\n    --secondary: 217.2 32.6% 17.5%;\n    --secondary-foreground: 210 40% 98%;\n\n    --muted: 217.2 32.6% 17.5%;\n    --muted-foreground: 215 20.2% 65.1%;\n\n    --accent: 217.2 32.6% 17.5%;\n    --accent-foreground: 210 40% 98%;\n\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 217.2 32.6% 17.5%;\n    --input: 217.2 32.6% 17.5%;\n    --ring: 212.7 26.8% 83.9%;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n\n.grainy {\n  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAElBMVEUAAAD8/vz08vT09vT8+vzs7uxH16TeAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAuFJREFUOI0Vk+3NLiEIRG1B8ClAYAsQ2AIEt4D9ePtv5Xp/mZgYJ2fOFJKEfInkVWY2aglmQFkimRTV7MblYyVqD7HXyhKsSuPX12MeDhRHLtGvRG+P+B/S0Vu4OswR9tmvwNPyhdCDbVayJGads/WiUWcjCvCnruTBNHS9gmX2VzVbk7ZvB1gb1hkWFGl+A/n+/FowcO34U/XvKqZ/fHY+6vgRfU92XrOBUbGeeDfQmjWjdrK+frc6FdGReQhfSF5JvR29O2QrfNw1huTwlgsyXLo0u+5So82sgv7tsFZR2nxB6lXiquHrfD8nfYZ9SeT0LiuvSoVrxGY16pCNRZKqvwWsn5OHypPBELzohMCaRaa0ceTHYqe7X/gfJEEtKFbJpWoNqO+aS1cuTykGPpK5Ga48m6L3NefTr013KqYBQu929iP1oQ/7UwSR+i3zqruUmT84qmhzLpxyj7pr9kg7LKvqaXxZmdpn+6o8sHqSqojy02gU3U8q9PnpidiaLks0mbMYz+q2uVXsoBQ8bfURULYxRgZVYCHMv9F4OA7qxT2NPPpvGQ/sTDH2yznKh7E2AcErfcNsaIoN1izzbJiaY63x4QjUFdBSvDCvugPpu5xDny0jzEeuUQbcP1aGT9V90uixngTRLYNEIIZ6yOF1H8tm7rj2JxiefsVy53zGVy3ag5uuPsdufYOzYxLRxngKe7nhx3VAq54pmz/DK9/Q3aDam2Yt3hNXB4HuU87jKNd/CKZn77Qdn5QkXPfqSkhk7hGOXXB+7v09KbBbqdvxGqa0AqfK/atIrL2WXdAgXAJ43Wtwe/aIoacXezeGPMlhDOHDbSfHnaXsL2QzbT82GRwZuezdwcoWzx5pnOnGMUdHuiY7lhdyWzWiHnucLZQxYStMJbtcydHaQ6vtMbe0AcDbxG+QG14AL94xry4297xpy9Cpf1OoxZ740gHDfrK+gtsy0xabwJmfgtCeii79B6aj0SJeLbd7AAAAAElFTkSuQmCC);\n}\n\n.tiptap.ProseMirror {\n  outline: none;\n}\n\n.is-active {\n  background-color: #dadada;\n  border-radius: 2px;\n}\n"
  },
  {
    "path": "src/app/layout.tsx",
    "content": "import \"./globals.css\";\nimport type { Metadata } from \"next\";\nimport { Inter } from \"next/font/google\";\nimport { ClerkProvider } from \"@clerk/nextjs\";\nimport Provider from \"@/components/Provider\";\n\nconst inter = Inter({ subsets: [\"latin\"] });\n\nexport const metadata: Metadata = {\n  title: \"AIdeation YT\",\n};\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <ClerkProvider>\n      <html lang=\"en\">\n        <Provider>\n          <body className={inter.className}>{children}</body>\n        </Provider>\n      </html>\n    </ClerkProvider>\n  );\n}\n"
  },
  {
    "path": "src/app/notebook/[noteId]/page.tsx",
    "content": "import DeleteButton from \"@/components/DeleteButton\";\nimport TipTapEditor from \"@/components/TipTapEditor\";\nimport { Button } from \"@/components/ui/button\";\nimport { clerk } from \"@/lib/clerk-server\";\nimport { db } from \"@/lib/db\";\nimport { $notes } from \"@/lib/db/schema\";\nimport { auth } from \"@clerk/nextjs\";\nimport { and, eq } from \"drizzle-orm\";\nimport Link from \"next/link\";\nimport { redirect } from \"next/navigation\";\nimport React from \"react\";\n\ntype Props = {\n  params: {\n    noteId: string;\n  };\n};\n\nconst NotebookPage = async ({ params: { noteId } }: Props) => {\n  const { userId } = await auth();\n  if (!userId) {\n    return redirect(\"/dashboard\");\n  }\n  const user = await clerk.users.getUser(userId);\n  const notes = await db\n    .select()\n    .from($notes)\n    .where(and(eq($notes.id, parseInt(noteId)), eq($notes.userId, userId)));\n\n  if (notes.length != 1) {\n    return redirect(\"/dashboard\");\n  }\n  const note = notes[0];\n\n  return (\n    <div className=\"min-h-screen grainy p-8\">\n      <div className=\"max-w-4xl mx-auto\">\n        <div className=\"border shadow-xl border-stone-200 rounded-lg p-4 flex items-center\">\n          <Link href=\"/dashboard\">\n            <Button className=\"bg-green-600\" size=\"sm\">\n              Back\n            </Button>\n          </Link>\n          <div className=\"w-3\"></div>\n          <span className=\"font-semibold\">\n            {user.firstName} {user.lastName}\n          </span>\n          <span className=\"inline-block mx-1\">/</span>\n          <span className=\"text-stone-500 font-semibold\">{note.name}</span>\n          <div className=\"ml-auto\">\n            <DeleteButton noteId={note.id} />\n          </div>\n        </div>\n\n        <div className=\"h-4\"></div>\n        <div className=\"border-stone-200 shadow-xl border rounded-lg px-16 py-8 w-full\">\n          <TipTapEditor note={note} />\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default NotebookPage;\n"
  },
  {
    "path": "src/app/page.tsx",
    "content": "import TypewriterTitle from \"@/components/ui/TypewriterTitle\";\nimport { Button } from \"@/components/ui/button\";\nimport Link from \"next/link\";\nimport { ArrowRight } from \"lucide-react\";\n\nexport default function Home() {\n  return (\n    <div className=\"bg-gradient-to-r min-h-screen grainy from-rose-100 to-teal-100\">\n      <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\">\n        <h1 className=\"font-semibold text-7xl text-center\">\n          AI <span className=\"text-green-600 font-bold\">note taking</span>{\" \"}\n          assistant.\n        </h1>\n        <div className=\"mt-4\"></div>\n        <h2 className=\"font-semibold text-3xl text-center text-slate-700\">\n          <TypewriterTitle />\n        </h2>\n        <div className=\"mt-8\"></div>\n\n        <div className=\"flex justify-center\">\n          <Link href=\"/dashboard\">\n            <Button className=\"bg-green-600\">\n              Get Started\n              <ArrowRight className=\"ml-2 w-5 h-5\" strokeWidth={3} />\n            </Button>\n          </Link>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/app/sign-in/[[...sign-in]]/page.tsx",
    "content": "import { SignIn } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return (\n    <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\">\n      <SignIn />\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/app/sign-up/[[...sign-up]]/page.tsx",
    "content": "import { SignUp } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return (\n    <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\">\n      <SignUp />\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/CreateNoteDialog.tsx",
    "content": "\"use client\";\nimport React from \"react\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"./ui/dialog\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { Input } from \"./ui/input\";\nimport axios from \"axios\";\nimport { Button } from \"./ui/button\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport { useRouter } from \"next/navigation\";\n\ntype Props = {};\n\nconst CreateNoteDialog = (props: Props) => {\n  const router = useRouter();\n  const [input, setInput] = React.useState(\"\");\n  const uploadToFirebase = useMutation({\n    mutationFn: async (noteId: string) => {\n      const response = await axios.post(\"/api/uploadToFirebase\", {\n        noteId,\n      });\n      return response.data;\n    },\n  });\n  const createNotebook = useMutation({\n    mutationFn: async () => {\n      const response = await axios.post(\"/api/createNoteBook\", {\n        name: input,\n      });\n      return response.data;\n    },\n  });\n\n  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {\n    e.preventDefault();\n    if (input === \"\") {\n      window.alert(\"Please enter a name for your notebook\");\n      return;\n    }\n    createNotebook.mutate(undefined, {\n      onSuccess: ({ note_id }) => {\n        console.log(\"created new note:\", { note_id });\n        // hit another endpoint to uplod the temp dalle url to permanent firebase url\n        uploadToFirebase.mutate(note_id);\n        router.push(`/notebook/${note_id}`);\n      },\n      onError: (error) => {\n        console.error(error);\n        window.alert(\"Failed to create new notebook\");\n      },\n    });\n  };\n\n  return (\n    <Dialog>\n      <DialogTrigger>\n        <div className=\"border-dashed border-2 flex border-green-600 h-full rounded-lg items-center justify-center sm:flex-col hover:shadow-xl transition hover:-translate-y-1 flex-row p-4\">\n          <Plus className=\"w-6 h-6 text-green-600\" strokeWidth={3} />\n          <h2 className=\"font-semibold text-green-600 sm:mt-2\">\n            New Note Book\n          </h2>\n        </div>\n      </DialogTrigger>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle>New Note Book</DialogTitle>\n          <DialogDescription>\n            You can create a new note by clicking the button below.\n          </DialogDescription>\n        </DialogHeader>\n        <form onSubmit={handleSubmit}>\n          <Input\n            value={input}\n            onChange={(e) => setInput(e.target.value)}\n            placeholder=\"Name...\"\n          />\n          <div className=\"h-4\"></div>\n          <div className=\"flex items-center gap-2\">\n            <Button type=\"reset\" variant={\"secondary\"}>\n              Cancel\n            </Button>\n            <Button\n              type=\"submit\"\n              className=\"bg-green-600\"\n              disabled={createNotebook.isLoading}\n            >\n              {createNotebook.isLoading && (\n                <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n              )}\n              Create\n            </Button>\n          </div>\n        </form>\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nexport default CreateNoteDialog;\n"
  },
  {
    "path": "src/components/DeleteButton.tsx",
    "content": "\"use client\";\nimport React from \"react\";\nimport { Button } from \"./ui/button\";\nimport { Trash } from \"lucide-react\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\n\ntype Props = {\n  noteId: number;\n};\n\nconst DeleteButton = ({ noteId }: Props) => {\n  const router = useRouter();\n  const deleteNote = useMutation({\n    mutationFn: async () => {\n      const response = await axios.post(\"/api/deleteNote\", {\n        noteId,\n      });\n      return response.data;\n    },\n  });\n  return (\n    <Button\n      variant={\"destructive\"}\n      size=\"sm\"\n      disabled={deleteNote.isLoading}\n      onClick={() => {\n        const confirm = window.confirm(\n          \"Are you sure you want to delete this note?\"\n        );\n        if (!confirm) return;\n        deleteNote.mutate(undefined, {\n          onSuccess: () => {\n            router.push(\"/dashboard\");\n          },\n          onError: (err) => {\n            console.error(err);\n          },\n        });\n      }}\n    >\n      <Trash />\n    </Button>\n  );\n};\n\nexport default DeleteButton;\n"
  },
  {
    "path": "src/components/Provider.tsx",
    "content": "\"use client\";\nimport React from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\n\ntype Props = {\n  children: React.ReactNode;\n};\n\nconst queryClient = new QueryClient();\n\nconst Provider = ({ children }: Props) => {\n  return (\n    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n  );\n};\n\nexport default Provider;\n"
  },
  {
    "path": "src/components/TipTapEditor.tsx",
    "content": "\"use client\";\nimport React from \"react\";\nimport { EditorContent, useEditor } from \"@tiptap/react\";\nimport { StarterKit } from \"@tiptap/starter-kit\";\nimport TipTapMenuBar from \"./TipTapMenuBar\";\nimport { Button } from \"./ui/button\";\nimport { useDebounce } from \"@/lib/useDebounce\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport Text from \"@tiptap/extension-text\";\nimport axios from \"axios\";\nimport { NoteType } from \"@/lib/db/schema\";\nimport { useCompletion } from \"ai/react\";\n\ntype Props = { note: NoteType };\n\nconst TipTapEditor = ({ note }: Props) => {\n  const [editorState, setEditorState] = React.useState(\n    note.editorState || `<h1>${note.name}</h1>`\n  );\n  const { complete, completion } = useCompletion({\n    api: \"/api/completion\",\n  });\n  const saveNote = useMutation({\n    mutationFn: async () => {\n      const response = await axios.post(\"/api/saveNote\", {\n        noteId: note.id,\n        editorState,\n      });\n      return response.data;\n    },\n  });\n  const customText = Text.extend({\n    addKeyboardShortcuts() {\n      return {\n        \"Shift-a\": () => {\n          // take the last 30 words\n          const prompt = this.editor.getText().split(\" \").slice(-30).join(\" \");\n          complete(prompt);\n          return true;\n        },\n      };\n    },\n  });\n\n  const editor = useEditor({\n    autofocus: true,\n    extensions: [StarterKit, customText],\n    content: editorState,\n    onUpdate: ({ editor }) => {\n      setEditorState(editor.getHTML());\n    },\n  });\n  const lastCompletion = React.useRef(\"\");\n\n  React.useEffect(() => {\n    if (!completion || !editor) return;\n    const diff = completion.slice(lastCompletion.current.length);\n    lastCompletion.current = completion;\n    editor.commands.insertContent(diff);\n  }, [completion, editor]);\n\n  const debouncedEditorState = useDebounce(editorState, 500);\n  React.useEffect(() => {\n    // save to db\n    if (debouncedEditorState === \"\") return;\n    saveNote.mutate(undefined, {\n      onSuccess: (data) => {\n        console.log(\"success update!\", data);\n      },\n      onError: (err) => {\n        console.error(err);\n      },\n    });\n  }, [debouncedEditorState]);\n  return (\n    <>\n      <div className=\"flex\">\n        {editor && <TipTapMenuBar editor={editor} />}\n        <Button disabled variant={\"outline\"}>\n          {saveNote.isLoading ? \"Saving...\" : \"Saved\"}\n        </Button>\n      </div>\n\n      <div className=\"prose prose-sm w-full mt-4\">\n        <EditorContent editor={editor} />\n      </div>\n      <div className=\"h-4\"></div>\n      <span className=\"text-sm\">\n        Tip: Press{\" \"}\n        <kbd className=\"px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg\">\n          Shift + A\n        </kbd>{\" \"}\n        for AI autocomplete\n      </span>\n    </>\n  );\n};\n\nexport default TipTapEditor;\n"
  },
  {
    "path": "src/components/TipTapMenuBar.tsx",
    "content": "import { Editor } from \"@tiptap/react\";\nimport {\n  Bold,\n  Code,\n  CodepenIcon,\n  Heading1,\n  Heading2,\n  Heading3,\n  Heading4,\n  Heading5,\n  Heading6,\n  Italic,\n  List,\n  ListOrdered,\n  Quote,\n  Redo,\n  Strikethrough,\n  Undo,\n} from \"lucide-react\";\n\nconst TipTapMenuBar = ({ editor }: { editor: Editor }) => {\n  return (\n    <div className=\"flex flex-wrap gap-2\">\n      <button\n        onClick={() => editor.chain().focus().toggleBold().run()}\n        disabled={!editor.can().chain().focus().toggleBold().run()}\n        className={editor.isActive(\"bold\") ? \"is-active\" : \"\"}\n      >\n        <Bold className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleItalic().run()}\n        disabled={!editor.can().chain().focus().toggleItalic().run()}\n        className={editor.isActive(\"italic\") ? \"is-active\" : \"\"}\n      >\n        <Italic className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleStrike().run()}\n        disabled={!editor.can().chain().focus().toggleStrike().run()}\n        className={editor.isActive(\"strike\") ? \"is-active\" : \"\"}\n      >\n        <Strikethrough className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleCode().run()}\n        disabled={!editor.can().chain().focus().toggleCode().run()}\n        className={editor.isActive(\"code\") ? \"is-active\" : \"\"}\n      >\n        <Code className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n        className={editor.isActive(\"heading\", { level: 1 }) ? \"is-active\" : \"\"}\n      >\n        <Heading1 className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n        className={editor.isActive(\"heading\", { level: 2 }) ? \"is-active\" : \"\"}\n      >\n        <Heading2 className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}\n        className={editor.isActive(\"heading\", { level: 3 }) ? \"is-active\" : \"\"}\n      >\n        <Heading3 className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleHeading({ level: 4 }).run()}\n        className={editor.isActive(\"heading\", { level: 4 }) ? \"is-active\" : \"\"}\n      >\n        <Heading4 className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleHeading({ level: 5 }).run()}\n        className={editor.isActive(\"heading\", { level: 5 }) ? \"is-active\" : \"\"}\n      >\n        <Heading5 className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleHeading({ level: 6 }).run()}\n        className={editor.isActive(\"heading\", { level: 6 }) ? \"is-active\" : \"\"}\n      >\n        <Heading6 className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleBulletList().run()}\n        className={editor.isActive(\"bulletList\") ? \"is-active\" : \"\"}\n      >\n        <List className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleOrderedList().run()}\n        className={editor.isActive(\"orderedList\") ? \"is-active\" : \"\"}\n      >\n        <ListOrdered className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleCodeBlock().run()}\n        className={editor.isActive(\"codeBlock\") ? \"is-active\" : \"\"}\n      >\n        <CodepenIcon className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().toggleBlockquote().run()}\n        className={editor.isActive(\"blockquote\") ? \"is-active\" : \"\"}\n      >\n        <Quote className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().undo().run()}\n        disabled={!editor.can().chain().focus().undo().run()}\n      >\n        <Undo className=\"w-6 h-6\" />\n      </button>\n      <button\n        onClick={() => editor.chain().focus().redo().run()}\n        disabled={!editor.can().chain().focus().redo().run()}\n      >\n        <Redo className=\"w-6 h-6\" />\n      </button>\n    </div>\n  );\n};\n\nexport default TipTapMenuBar;\n"
  },
  {
    "path": "src/components/ui/TypewriterTitle.tsx",
    "content": "\"use client\";\nimport React from \"react\";\nimport Typewriter from \"typewriter-effect\";\n\ntype Props = {};\n\nconst TypewriterTitle = (props: Props) => {\n  return (\n    <Typewriter\n      options={{\n        loop: true,\n      }}\n      onInit={(typewriter) => {\n        typewriter\n          .typeString(\"🚀 Supercharged Productivity.\")\n          .pauseFor(1000)\n          .deleteAll()\n          .typeString(\"🤖 AI-Powered Insights.\")\n          .start();\n      }}\n    />\n  );\n};\n\nexport default TypewriterTitle;\n"
  },
  {
    "path": "src/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 rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\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": "src/components/ui/dialog.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Dialog = DialogPrimitive.Root\n\nconst DialogTrigger = DialogPrimitive.Trigger\n\nconst DialogPortal = ({\n  className,\n  ...props\n}: DialogPrimitive.DialogPortalProps) => (\n  <DialogPrimitive.Portal className={cn(className)} {...props} />\n)\nDialogPortal.displayName = DialogPrimitive.Portal.displayName\n\nconst DialogOverlay = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    ref={ref}\n    className={cn(\n      \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n      className\n    )}\n    {...props}\n  />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\nconst DialogContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n  <DialogPortal>\n    <DialogOverlay />\n    <DialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n        <X className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Close</span>\n      </DialogPrimitive.Close>\n    </DialogPrimitive.Content>\n  </DialogPortal>\n))\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\nconst DialogHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-1.5 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nDialogHeader.displayName = \"DialogHeader\"\n\nconst DialogFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    ref={ref}\n    className={cn(\n      \"text-lg font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\nexport {\n  Dialog,\n  DialogTrigger,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n}\n"
  },
  {
    "path": "src/components/ui/input.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n  extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, type, ...props }, ref) => {\n    return (\n      <input\n        type={type}\n        className={cn(\n          \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"
  },
  {
    "path": "src/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": "src/lib/clerk-server.ts",
    "content": "import { Clerk } from \"@clerk/backend\";\n\nexport const clerk = Clerk({\n  apiKey: process.env.CLERK_SECRET_KEY,\n});\n"
  },
  {
    "path": "src/lib/db/index.ts",
    "content": "import { neon, neonConfig } from \"@neondatabase/serverless\";\nimport { drizzle } from \"drizzle-orm/neon-http\";\n\nneonConfig.fetchConnectionCache = true;\n\nif (!process.env.DATABASE_URL) {\n  throw new Error(\"DATABASE_URL is not defined\");\n}\n\nconst sql = neon(process.env.DATABASE_URL);\n\nexport const db = drizzle(sql);\n"
  },
  {
    "path": "src/lib/db/schema.ts",
    "content": "import { pgTable, serial, text, timestamp } from \"drizzle-orm/pg-core\";\n\nexport const $notes = pgTable(\"notes\", {\n  id: serial(\"id\").primaryKey(),\n  name: text(\"name\").notNull(),\n  createdAt: timestamp(\"created_at\").notNull().defaultNow(),\n  imageUrl: text(\"imageUrl\"),\n  userId: text(\"user_id\").notNull(),\n  editorState: text(\"editor_state\"),\n});\n\nexport type NoteType = typeof $notes.$inferInsert;\n\n// drizzle-orm\n// drizzle-kit\n"
  },
  {
    "path": "src/lib/firebase.ts",
    "content": "// Import the functions you need from the SDKs you need\nimport { initializeApp } from \"firebase/app\";\nimport { getDownloadURL, getStorage, ref, uploadBytes } from \"firebase/storage\";\n// TODO: Add SDKs for Firebase products that you want to use\n// https://firebase.google.com/docs/web/setup#available-libraries\n\n// Your web app's Firebase configuration\nconst firebaseConfig = {\n  apiKey: process.env.FIREBASE_API_KEY,\n  authDomain: \"aideation-yt.firebaseapp.com\",\n  projectId: \"aideation-yt\",\n  storageBucket: \"aideation-yt.appspot.com\",\n  messagingSenderId: \"962348384448\",\n  appId: \"1:962348384448:web:e02758407aba3258d5ad25\",\n};\n\n// Initialize Firebase\nconst app = initializeApp(firebaseConfig);\nexport const storage = getStorage(app);\n\nexport async function uploadFileToFirebase(image_url: string, name: string) {\n  try {\n    const response = await fetch(image_url);\n    const buffer = await response.arrayBuffer();\n    const file_name = name.replace(\" \", \"\") + Date.now + \".jpeg\";\n    const storageRef = ref(storage, file_name);\n    await uploadBytes(storageRef, buffer, {\n      contentType: \"image/jpeg\",\n    });\n    const firebase_url = await getDownloadURL(storageRef);\n    return firebase_url;\n  } catch (error) {\n    console.error(error);\n  }\n}\n"
  },
  {
    "path": "src/lib/openai.ts",
    "content": "import { Configuration, OpenAIApi } from \"openai-edge\";\n\nconst config = new Configuration({\n  apiKey: process.env.OPENAI_API_KEY,\n});\n\nconst openai = new OpenAIApi(config);\n\nexport async function generateImagePrompt(name: string) {\n  try {\n    const response = await openai.createChatCompletion({\n      model: \"gpt-3.5-turbo\",\n      messages: [\n        {\n          role: \"system\",\n          content:\n            \"You are an creative and helpful AI assistance capable of generating interesting thumbnail descriptions for my notes. Your output will be fed into the DALLE API to generate a thumbnail. The description should be minimalistic and flat styled\",\n        },\n        {\n          role: \"user\",\n          content: `Please generate a thumbnail description for my notebook titles ${name}`,\n        },\n      ],\n    });\n    const data = await response.json();\n    const image_description = data.choices[0].message.content;\n    return image_description as string;\n  } catch (error) {\n    console.log(error);\n    throw error;\n  }\n}\n\nexport async function generateImage(image_description: string) {\n  try {\n    const response = await openai.createImage({\n      prompt: image_description,\n      n: 1,\n      size: \"256x256\",\n    });\n    const data = await response.json();\n    const image_url = data.data[0].url;\n    return image_url as string;\n  } catch (error) {\n    console.error(error);\n  }\n}\n"
  },
  {
    "path": "src/lib/useDebounce.ts",
    "content": "import React from \"react\";\n\nexport function useDebounce(value: string, delay: number) {\n  const [debouncedValue, setDebouncedValue] = React.useState(value);\n\n  React.useEffect(() => {\n    const handler = setTimeout(() => {\n      setDebouncedValue(value);\n    }, delay);\n    return () => {\n      clearTimeout(handler);\n    };\n  }, [value, delay]);\n\n  return debouncedValue;\n}\n"
  },
  {
    "path": "src/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n \nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"
  },
  {
    "path": "src/middleware.ts",
    "content": "import { authMiddleware } from \"@clerk/nextjs\";\n\n// This example protects all routes including api/trpc routes\n// Please edit this to allow other routes to be public as needed.\n// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your middleware\nexport default authMiddleware({\n  publicRoutes: [\"/\"],\n});\n\nexport const config = {\n  matcher: [\"/((?!.+\\\\.[\\\\w]+$|_next).*)\", \"/\", \"/(api|trpc)(.*)\"],\n};\n"
  },
  {
    "path": "tailwind.config.ts",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: [\"class\"],\n  content: [\n    \"./pages/**/*.{ts,tsx}\",\n    \"./components/**/*.{ts,tsx}\",\n    \"./app/**/*.{ts,tsx}\",\n    \"./src/**/*.{ts,tsx}\",\n  ],\n  theme: {\n    container: {\n      center: true,\n      padding: \"2rem\",\n      screens: {\n        \"2xl\": \"1400px\",\n      },\n    },\n    extend: {\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n      },\n      borderRadius: {\n        lg: \"var(--radius)\",\n        md: \"calc(var(--radius) - 2px)\",\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n      keyframes: {\n        \"accordion-down\": {\n          from: { height: 0 },\n          to: { height: \"var(--radix-accordion-content-height)\" },\n        },\n        \"accordion-up\": {\n          from: { height: \"var(--radix-accordion-content-height)\" },\n          to: { height: 0 },\n        },\n      },\n      animation: {\n        \"accordion-down\": \"accordion-down 0.2s ease-out\",\n        \"accordion-up\": \"accordion-up 0.2s ease-out\",\n      },\n    },\n  },\n  plugins: [require(\"tailwindcss-animate\"), require(\"@tailwindcss/typography\")],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\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      \"@/*\": [\"./src/*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  }
]