<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rajan Shrestha</title>
    <description>The latest articles on Forem by Rajan Shrestha (@rajanshrestha).</description>
    <link>https://forem.com/rajanshrestha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F844125%2F82a68466-cd62-48ee-a428-0a2f0e825ce9.jpeg</url>
      <title>Forem: Rajan Shrestha</title>
      <link>https://forem.com/rajanshrestha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rajanshrestha"/>
    <language>en</language>
    <item>
      <title>Build An AI-Powered Code Generator (Nextjs, CopilotKit, gemini-pro, Langchain)</title>
      <dc:creator>Rajan Shrestha</dc:creator>
      <pubDate>Tue, 07 May 2024 11:19:54 +0000</pubDate>
      <link>https://forem.com/rajanshrestha/build-an-ai-powered-code-generator-nextjs-copilotkit-gemini-pro-langchain-44j9</link>
      <guid>https://forem.com/rajanshrestha/build-an-ai-powered-code-generator-nextjs-copilotkit-gemini-pro-langchain-44j9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Struggling with the daunting task of finding precise code solutions, I created &lt;a href="https://code-gpt-rose.vercel.app/" rel="noopener noreferrer"&gt;CodeGPT&lt;/a&gt;. This AI-powered code generator swiftly converts natural language descriptions into code snippets, alleviating the frustration of tedious searches. Say goodbye to coding woes and embrace effortless solutions with CodeGPT.&lt;/p&gt;

&lt;p&gt;We'll cover how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;build web applications with Next.js, &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;langchain, Gemini for generative AI capabilities,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;integrate AI into software applications with CopilotKit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into this tutorial, make sure you have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Basic understanding of programming.&lt;/li&gt;
&lt;li&gt;Familiarity with Next.js, TypeScript, and Tailwind CSS.&lt;/li&gt;
&lt;li&gt;Comfortable using command line for npm and Git.&lt;/li&gt;
&lt;li&gt;Node.js and npm installed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Project Set up and Package Installation
&lt;/h2&gt;

&lt;p&gt;To kickstart your project, let's begin by creating a Next.js application. Open your terminal and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app codeGPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flytyi5p9r8u0astbwlo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flytyi5p9r8u0astbwlo2.png" alt="Image description" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's install the CopilotKit packages. These packages enable us to interact with the React state and integrate an AI copilot into our application:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/react-core @copilotkit/backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For authentication, you can use &lt;a href="https://authjs.dev/getting-started/installation?framework=next.js" rel="noopener noreferrer"&gt;Auth.js &lt;/a&gt; with &lt;a href="https://orm.drizzle.team/docs/get-started-sqlite#turso" rel="noopener noreferrer"&gt;Drizzle ORM&lt;/a&gt; to store message. &lt;br&gt;
For database, you can use &lt;a href="https://turso.tech/" rel="noopener noreferrer"&gt;tuso.tech&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Building CodeGPT
&lt;/h2&gt;

&lt;p&gt;In this section, I'll walk through the api endpoint development with the chat ui. &lt;/p&gt;

&lt;p&gt;File structure&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-src
----|-app
------|-api
--------|-auth
----------|-[[...nextauth]]
------------|-route.tsx
--------|-copilotkit
----------|-route.ts
------|-layout.tsx
------|-page.tsx
------|-action.ts
------|-chat
--------|-[id]
----------|-page.tsx
--------|-new-chat.tsx
----|-components
------|-ui
------|-chat-content.tsx
------|-chat-list.tsx
------|-growing-text-area.tsx
------|-NavBar.tsx
----|-db
------|-index.ts
------|-schema.ts
----|-lib
------|-utils.ts
----|-auth.ts
|-drizzle.config.ts
|-.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  API Endpoint
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/app/api/copilotkit/route.ts&lt;/code&gt;&lt;br&gt;
POST /route&lt;br&gt;
This endpoint is part of the Copilot Kit and uses Google's Generative AI to generate responses based on the provided input.&lt;/p&gt;

&lt;p&gt;Request&lt;br&gt;
The request body should contain a forwardedProps object. If forwardedProps.choices is undefined, it will be set to an empty array. If forwardedProps.choices is not an array, an error will be returned.&lt;/p&gt;

&lt;p&gt;Response&lt;br&gt;
The endpoint responds with a string generated by Google's Generative AI. The AI is given a prompt that describes the expected behavior of the AI, followed by the choices provided in forwardedProps.choices.&lt;/p&gt;

&lt;p&gt;If an error occurs during the generation of the response, the string "An error occurred" is returned. If forwardedProps.choices is not an array, the string "Invalid input: forwardedProps.choices is not an array" is returned.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  CopilotRuntime,
  LangChainAdapter,
  LangChainReturnType,
} from "@copilotkit/backend";
import { GoogleGenerativeAI } from "@google/generative-ai";
import { StreamingTextResponse } from "ai";

const genAI = new GoogleGenerativeAI(process.env.GENERATIVE_AI_API_KEY!);

export async function POST(req: Request) {
  const copilotKit = new CopilotRuntime();
  return copilotKit.response(
    req,
    new LangChainAdapter(
      async (forwardedProps): Promise&amp;lt;LangChainReturnType&amp;gt; =&amp;gt; {
        console.log("forwardedProps", forwardedProps);

        // If forwardedProps.choices is undefined, set it to an empty array
        if (forwardedProps.choices === undefined) {
          forwardedProps.choices = [];
        }

        // Check if forwardedProps.choices is an array
        if (Array.isArray(forwardedProps.choices)) {
          // Transform forwardedProps into the expected format
          const prompt =
            `As an expert in software development, you excel in utilizing the latest technologies and methodologies across all programming languages and frameworks. Your expertise extends to creating visually appealing and functionally robust UI designs. Your responses are exclusively in code form, focusing on delivering comprehensive, executable solutions without explanatory text. Your approach emphasizes code clarity, adherence to best practices, and the use of cutting-edge tools. You meticulously follow specified requirements regarding libraries and languages, ensuring your code contributions are fully integrated and operational within the given context. Your primary objective is to communicate solely through code, providing complete and self-sufficient code responses that align with the user's directives.` +
            `I am in the process of enhancing a pre-existing application with new functionalities and improvements. Your assistance is sought for introducing new features and refining the current codebase to elevate its readability and adherence to contemporary best practices. The expectation is for the solutions to embody the latest advancements in software development, tailored to the specified technologies and frameworks. Your contributions should be comprehensive, avoiding partial or differential code snippets, and should be ready to run or compile as provided. It is imperative that your code aligns with the project's existing language, style, and libraries, unless a conversion or transformation is requested. Your responses should be purely code-based, fulfilling the requirement to communicate exclusively through well-structured and complete code examples.`;

          try {
            const modelInstance = genAI.getGenerativeModel({
              model: "gemini-pro",
            });
            const chatCompletion = await modelInstance.generateContent([
              prompt + forwardedProps.choices.join(" "),
            ]);
            const responseStream = new ReadableStream({
              start(controller) {
                // Convert the string to a Uint8Array
                const uint8Array = new TextEncoder().encode(
                  chatCompletion.response.text()
                );
                controller.enqueue(uint8Array);
                controller.close();
              },
            });
            const streaming_to_string = new StreamingTextResponse(
              responseStream
            );

            return streaming_to_string.text();
          } catch (error) {
            // console.error(error);
            return "An error occurred";
          }
        } else {
          console.error("forwardedProps.choices is not an array");
          // Handle the error appropriately...
          // For example, you might want to return an error message:
          return "Invalid input: forwardedProps.choices is not an array";
        }
      }
    )
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ChatListPage Component
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/app/chat/[id]/page.tsx&lt;/code&gt;&lt;br&gt;
The ChatListPage is a React component that displays a list of chat messages for a specific user.&lt;/p&gt;

&lt;p&gt;Props&lt;br&gt;
The component accepts a single prop:&lt;/p&gt;

&lt;p&gt;params: An object that contains a single property id, which is the ID of the user whose messages should be displayed.&lt;br&gt;
Functionality&lt;br&gt;
The component first checks if the user is authenticated by calling the auth() function. If the user is not authenticated, it returns a div with the text "Not logged in".&lt;/p&gt;

&lt;p&gt;If the user is authenticated, it queries the usersMessages table in the database for messages that match the provided user ID. This is done using the db.select().from().where() function chain from the drizzle-orm library.&lt;/p&gt;

&lt;p&gt;The component then returns a div that maps over the chat array and returns a ChatContent component for each message. The ChatContent component is passed two props: session (the current user session) and content (the content of the message).&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { auth } from "@/auth";
import ChatContent from "@/components/chat-content";
import { db } from "@/db";
import { usersMessages } from "@/db/schema";
import { eq } from "drizzle-orm";
import React from "react";

const ChatListPage = async ({ params }: { params: { id: string } }) =&amp;gt; {
  const session = await auth();
  if (!session) {
    return (
      &amp;lt;div className="font-bold dark:text-white text-black"&amp;gt;Not logged in&amp;lt;/div&amp;gt;
    );
  }
  const chat = await db
    .select()
    .from(usersMessages)
    .where(eq(usersMessages.id, params.id));

  return (
    &amp;lt;div&amp;gt;
      {chat.map((chat) =&amp;gt; {
        return (
          &amp;lt;div className="flex flex-col gap-1" key={chat.id}&amp;gt;
            &amp;lt;ChatContent session={session} content={chat.message} /&amp;gt;
          &amp;lt;/div&amp;gt;
        );
      })}
    &amp;lt;/div&amp;gt;
  );
};

export default ChatListPage;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  NewChat Component
&lt;/h2&gt;

&lt;p&gt;The NewChat is a React component that provides a button for creating a new chat.&lt;/p&gt;

&lt;p&gt;Functionality&lt;br&gt;
When the "New Chat" button is clicked, the user is redirected to the root ("/") route of the application. This is done using the useRouter hook from Next.js, which provides access to the router object. The router.push("/") function is called when the button is clicked, which navigates to the root route.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
import React from "react";

const NewChat = () =&amp;gt; {
  const router = useRouter();
  return &amp;lt;Button onClick={() =&amp;gt; router.push("/")}&amp;gt;New Chat&amp;lt;/Button&amp;gt;;
};

export default NewChat;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  action.ts
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/app/action.ts&lt;/code&gt;&lt;br&gt;
This file contains two functions, storeMessage and Chatlist, which interact with a database to store and retrieve chat messages.&lt;/p&gt;

&lt;p&gt;storeMessage(user_id: string, content: string)&lt;br&gt;
This function takes a user_id and content as parameters and stores a new message in the usersMessages table in the database.&lt;/p&gt;

&lt;p&gt;First, it checks if a user with the given user_id exists in the users table. If the user does not exist, the function returns and does not store the message.&lt;/p&gt;

&lt;p&gt;If the user does exist, the function attempts to insert a new row into the usersMessages table with the userId, message, createdAt, and id fields. If an error occurs during this process, it is logged to the console and an Error is thrown.&lt;/p&gt;

&lt;p&gt;Chatlist(user_id: string)&lt;br&gt;
This function takes a user_id as a parameter and retrieves all messages from the usersMessages table in the database that match the given user_id.&lt;/p&gt;

&lt;p&gt;The function returns an array of messages.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use server";

import { db } from "@/db";
import { users, usersMessages } from "@/db/schema";
import { eq } from "drizzle-orm";

// store the message to the database
export async function storeMessage(user_id: string, content: string) {
  const User = await db
    .select({ users })
    .from(users)
    .where(eq(users.id, user_id));
  if (!User) return;
  try {
    await db.insert(usersMessages).values({
      userId: `${user_id}`,
      message: content,
      createdAt: new Date(),
      id: crypto.randomUUID(),
    });
  } catch (error) {
    console.error("Failed to store message: ", error);
    throw new Error("Failed to store message");
  }
}

export async function Chatlist(user_id: string) {
  const chat = await db
    .select()
    .from(usersMessages)
    .where(eq(usersMessages.userId, user_id));

  return chat;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  RootLayout and Page Component
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/layout.tsx&lt;/code&gt;&lt;br&gt;
The RootLayout component is a wrapper component that provides global styles and context to all child components.&lt;/p&gt;

&lt;p&gt;Props&lt;br&gt;
The component accepts a single prop:&lt;/p&gt;

&lt;p&gt;children: The child components to be rendered within the RootLayout.&lt;br&gt;
Functionality&lt;br&gt;
The RootLayout component sets up the global styles and context for the application. It uses the ThemeProvider component to provide a theme context to all child components. The NavBar component is rendered at the top of the page, and the CopilotKit component wraps the child components, providing them with access to the Copilot Kit's functionalities.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ChatContent from "../components/chat-content";
import { auth } from "@/auth";
import { Button } from "@/components/ui/button";
import Link from "next/link";

export default async function Page() {
  const session = await auth();

  if (!session) {
    return (
      &amp;lt;main className="flex flex-col gap-6 items-center justify-center h-screen"&amp;gt;
        &amp;lt;div className="flex flex-col gap-4 items-center justify-center overflow-x-auto w-[80vh] text-center"&amp;gt;
          &amp;lt;h1 className="text-2xl lg:text-4xl w-[50vh] font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-green-500 to-blue-500"&amp;gt;
            Streamline Your Coding Experience with CodeGPT
          &amp;lt;/h1&amp;gt;
          &amp;lt;p className="font-light w-[50vh]"&amp;gt;
            CodeGPT is the ultimate solution for developers looking to simplify
            their coding workflow. Our AI-powered web app generates code
            snippets instantly based on your natural language descriptions. With
            CodeGPT, coding has never been easier.
          &amp;lt;/p&amp;gt;
          &amp;lt;p className="text-md font-bold underline"&amp;gt;Get Started Today!&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;Link href={"api/auth/signin"}&amp;gt;
          &amp;lt;Button className=" text-center font-bold"&amp;gt;Log in&amp;lt;/Button&amp;gt;
        &amp;lt;/Link&amp;gt;
      &amp;lt;/main&amp;gt;
    );
  }
  return (
    &amp;lt;main className="max-h-screen"&amp;gt;
      &amp;lt;ChatContent session={session} /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;src/page.tsx&lt;/code&gt;&lt;br&gt;
The Page component is an asynchronous function that checks if the user is authenticated and renders different content based on the authentication status.&lt;/p&gt;

&lt;p&gt;Functionality&lt;br&gt;
The Page component first calls the auth function to check if the user is authenticated. If the user is not authenticated, it renders a landing page with a description of CodeGPT and a login button.&lt;/p&gt;

&lt;p&gt;If the user is authenticated, it renders the ChatContent component, passing the user's session as a prop.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ChatContent from "../components/chat-content";
import { auth } from "@/auth";
import { Button } from "@/components/ui/button";
import Link from "next/link";

export default async function Page() {
  const session = await auth();

  if (!session) {
    return (
      &amp;lt;main className="flex flex-col gap-6 items-center justify-center h-screen"&amp;gt;
        &amp;lt;div className="flex flex-col gap-4 items-center justify-center overflow-x-auto w-[80vh] text-center"&amp;gt;
          &amp;lt;h1 className="text-2xl lg:text-4xl w-[50vh] font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-green-500 to-blue-500"&amp;gt;
            Streamline Your Coding Experience with CodeGPT
          &amp;lt;/h1&amp;gt;
          &amp;lt;p className="font-light w-[50vh]"&amp;gt;
            CodeGPT is the ultimate solution for developers looking to simplify
            their coding workflow. Our AI-powered web app generates code
            snippets instantly based on your natural language descriptions. With
            CodeGPT, coding has never been easier.
          &amp;lt;/p&amp;gt;
          &amp;lt;p className="text-md font-bold underline"&amp;gt;Get Started Today!&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;Link href={"api/auth/signin"}&amp;gt;
          &amp;lt;Button className=" text-center font-bold"&amp;gt;Log in&amp;lt;/Button&amp;gt;
        &amp;lt;/Link&amp;gt;
      &amp;lt;/main&amp;gt;
    );
  }
  return (
    &amp;lt;main className="max-h-screen"&amp;gt;
      &amp;lt;ChatContent session={session} /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ChatContent Component
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/component/chat-content.tsx&lt;/code&gt;&lt;br&gt;
The ChatContent component is a React component that handles the chat functionality of the application.&lt;/p&gt;

&lt;p&gt;Props&lt;br&gt;
session: The user's session data.&lt;br&gt;
content: Optional initial content for the chat.&lt;br&gt;
Functionality&lt;br&gt;
The component maintains the state of the assistant's response, loading status, and copy button text. It handles the submission of chat input, stopping the chat, and copying the assistant's response to the clipboard. It also stores the assistant's response in the database.&lt;/p&gt;

&lt;p&gt;The component renders a chat interface, including the chat input and the assistant's response. The response is rendered as markdown, with syntax highlighting for code blocks. If there's no response yet, it displays a default message.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { useState, useRef } from "react";

import ChatInput from "@/components/chat-input";

import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { vscDarkPlus as dark } from "react-syntax-highlighter/dist/esm/styles/prism";

import { convertFileToBase64 } from "@/lib/utils";
import { Copy } from "lucide-react";
import { Button } from "./ui/button";
import { storeMessage } from "@/app/action";

type ChatContentProps = {
  content?: string;
  session: any;
};

export default function ChatContent({ session, content }: ChatContentProps) {
  const [assisnantResponse, setAssistantResponse] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const abortControllerRef = useRef&amp;lt;AbortController | null&amp;gt;(null);
  const [copyButtonText, setCopyButtonText] = useState("Copy");

  const handleSubmit = async (value: string, file?: File) =&amp;gt; {
    setIsLoading(true);
    setAssistantResponse("");

    let body = "";
    if (file) {
      const imageUrl = await convertFileToBase64(file);
      const content = [
        {
          type: "image_url",
          image_url: {
            url: imageUrl,
          },
        },
        {
          type: "text",
          text: value,
        },
      ];

      body = JSON.stringify({ content });
    } else {
      body = JSON.stringify({ content: value });
    }

    try {
      abortControllerRef.current = new AbortController();
      const res = await fetch("/api/copilotkit", {
        method: "POST",
        body: body,
        headers: {
          "Content-Type": "application/json",
        },
        signal: abortControllerRef.current.signal,
      });

      if (!res.ok || !res.body) {
        alert("Error sending message");
        return;
      }

      const reader = res.body.getReader();

      const decoder = new TextDecoder();
      while (true) {
        const { value, done } = await reader.read();

        const text = decoder.decode(value);
        setAssistantResponse((currentValue) =&amp;gt; currentValue + text);

        if (done) {
          break;
        }
      }
    } catch (error: any) {
      if (error.name !== "AbortError") {
        alert("Error sending message");
      }
    }
    abortControllerRef.current = null;
    setIsLoading(false);
  };

  const handleStop = () =&amp;gt; {
    if (!abortControllerRef.current) {
      return;
    }
    abortControllerRef.current.abort();
    abortControllerRef.current = null;
  };
  const copyMarkdownToClipboard = () =&amp;gt; {
    try {
      navigator.clipboard.writeText(assisnantResponse);
    } catch (error) {
      console.error("Failed to copy content: ", error);
    }
  };
  const handleCopyClick = async () =&amp;gt; {
    await copyMarkdownToClipboard();
    setCopyButtonText("Copied...");
    setTimeout(() =&amp;gt; setCopyButtonText("Copy"), 1000);
  };
  const userId = session.user.id;
  if (assisnantResponse.length &amp;gt; 0) {
    storeMessage(`${userId}`, assisnantResponse);
  }
  return (
    &amp;lt;div className="flex flex-col h-screen"&amp;gt;
      &amp;lt;div className="max-w-4xl w-full max-h-[70vh] mx-auto flex-1 px-10 py-5 overflow-x-hidden overflow-y-scroll custom-scrollbar prose dark:prose-invert"&amp;gt;
        {(content || assisnantResponse) &amp;amp;&amp;amp; (
          &amp;lt;div className="flex justify-end"&amp;gt;
            &amp;lt;Button onClick={handleCopyClick}&amp;gt;
              &amp;lt;Copy size={24} /&amp;gt;
              &amp;lt;span className="ml-2"&amp;gt;{copyButtonText}&amp;lt;/span&amp;gt;
            &amp;lt;/Button&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
        &amp;lt;Markdown
          remarkPlugins={[remarkGfm]}
          components={{
            code(props) {
              const { children, className, node, ...rest } = props;
              const match = /language-(\w+)/.exec(className || "");
              return match ? (
                &amp;lt;SyntaxHighlighter
                  PreTag="div"
                  language={match[1]}
                  style={dark}
                  wrapLines={true}
                  wrapLongLines={true}
                &amp;gt;
                  {String(children).replace(/\n$/, "")}
                &amp;lt;/SyntaxHighlighter&amp;gt;
              ) : (
                &amp;lt;code {...rest} className={className}&amp;gt;
                  &amp;lt;div className="overflow-y-auto"&amp;gt;{children}&amp;lt;/div&amp;gt;
                &amp;lt;/code&amp;gt;
              );
            },
          }}
        &amp;gt;
          {assisnantResponse || content}
        &amp;lt;/Markdown&amp;gt;
        {!assisnantResponse &amp;amp;&amp;amp; !content &amp;amp;&amp;amp; (
          &amp;lt;div className="text-center text-gray-400 flex flex-col gap-4"&amp;gt;
            &amp;lt;span&amp;gt;Ask me anything related to Code-gen! 🚀 &amp;lt;/span&amp;gt;
            &amp;lt;span&amp;gt;Enjoy the experience! 🎉&amp;lt;/span&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
      &amp;lt;ChatInput
        onSubmit={handleSubmit}
        isStreaming={isLoading}
        onStop={handleStop}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ExpandingInput Component
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/component/chat-input.tsx&lt;/code&gt;&lt;br&gt;
The ExpandingInput component is a React component that provides a text input field and an image selection field for user input. It also provides a submit button and a stop button for controlling the input submission. The component maintains the state of the input content and the selected image. The onSubmit and onStop callbacks are called when the submit and stop buttons are clicked, respectively.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { useState } from "react";
import GrowingTextArea from "./growing-text-area";
import { cn } from "@/lib/utils";

import ImageSelection from "./image-selection";

export default function ExpandingInput({
  onSubmit,
  onStop,
  isStreaming,
}: {
  onSubmit?: (value: string, file?: File) =&amp;gt; void;
  onStop?: () =&amp;gt; void;
  isStreaming?: boolean;
}) {
  const [content, setContent] = useState("");
  const [selectedImage, setSelectedImage] = useState&amp;lt;File | undefined&amp;gt;(
    undefined
  );

  const submit = (value: string) =&amp;gt; {
    onSubmit?.(value, selectedImage);
    setContent("");
    setSelectedImage(undefined);
  };
  const handleSubmit = (e: React.FormEvent) =&amp;gt; {
    e.preventDefault();
    submit(content);
  };

  const buttonDisabled = content.length === 0 || isStreaming;

  return (
    &amp;lt;div className="w-full"&amp;gt;
      &amp;lt;form
        onSubmit={handleSubmit}
        className="w-full flex flex-col gap-y-4 px-4 relative max-w-5xl mx-auto"
      &amp;gt;
        &amp;lt;ImageSelection
          selectedImage={selectedImage}
          setSelectedImage={setSelectedImage}
        /&amp;gt;
        &amp;lt;GrowingTextArea
          className="w-full bg-transparent border border-gray-500 rounded-2xl outline-none resize-none pl-12 pr-14 py-4 scrollbar-content overflow-y-auto overflow-x-clip overscroll-contain"
          value={content}
          onChange={(e) =&amp;gt; setContent(e.target.value)}
        /&amp;gt;
        {isStreaming ? (
          &amp;lt;button
            type="button"
            onClick={onStop}
            className="flex absolute right-0 bottom-0 px-1 py-1 mr-7 mb-2 rounded-2xl z-10 w-10 h-10 items-center justify-center dark:fill-neutral-300 :fill-neutral-700 dark:hover:fill-neutral-100 hover:fill-neutral-900 transition-all"
          &amp;gt;
            &amp;lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&amp;gt;
              &amp;lt;path d="m12,0C5.383,0,0,5.383,0,12s5.383,12,12,12,12-5.383,12-12S18.617,0,12,0Zm0,22c-5.514,0-10-4.486-10-10S6.486,2,12,2s10,4.486,10,10-4.486,10-10,10Zm2-15h-4c-1.654,0-3,1.346-3,3v4c0,1.654,1.346,3,3,3h4c1.654,0,3-1.346,3-3v-4c0-1.654-1.346-3-3-3Zm1,7c0,.551-.449,1-1,1h-4c-.551,0-1-.449-1-1v-4c0-.551.449-1,1-1h4c.551,0,1,.449,1,1v4Z" /&amp;gt;
            &amp;lt;/svg&amp;gt;
          &amp;lt;/button&amp;gt;
        ) : (
          &amp;lt;button
            className={cn(
              "flex absolute right-0 bottom-0 px-1 py-1 mr-7 mb-2 dark:bg-white bg-black rounded-2xl z-10 w-10 h-10 items-center justify-center",
              buttonDisabled &amp;amp;&amp;amp; "opacity-50"
            )}
            disabled={buttonDisabled}
            type="submit"
          &amp;gt;
            &amp;lt;svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              fill="none"
              className="text-white dark:text-black w-7 h-7"
            &amp;gt;
              &amp;lt;title&amp;gt;Submit&amp;lt;/title&amp;gt;
              &amp;lt;path
                d="M7 11L12 6L17 11M12 18V7"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              &amp;gt;&amp;lt;/path&amp;gt;
            &amp;lt;/svg&amp;gt;
          &amp;lt;/button&amp;gt;
        )}
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ChatList Component
&lt;/h3&gt;

&lt;p&gt;The ChatList component is a React component that displays a list of chat messages for a logged-in user. If the user is not logged in, it displays a login prompt. The chat messages are fetched from a database. Each message is a link that navigates to a detailed view of the chat.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { auth } from "@/auth";
import Link from "next/link";
import { Button } from "./ui/button";
import { db } from "@/db";
import { usersMessages } from "@/db/schema";
import { eq } from "drizzle-orm";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "./ui/separator";

export const dynamic = "force-dynamic";

export default async function ChatList() {
  const session = await auth();
  if (!session) {
    return (
      &amp;lt;div className="flex flex-col gap-4 text-center"&amp;gt;
        &amp;lt;p className="font-bold"&amp;gt;Not logged in&amp;lt;/p&amp;gt;
        &amp;lt;Link href="/api/auth/signin"&amp;gt;
          &amp;lt;Button&amp;gt;Login&amp;lt;/Button&amp;gt;
        &amp;lt;/Link&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
  const chat = await db
    .select()
    .from(usersMessages)
    .where(eq(usersMessages.userId, session.user?.id!));

  return (
    &amp;lt;ScrollArea className="h-[90vh] max-w-full rounded-md "&amp;gt;
      &amp;lt;div className="p-4"&amp;gt;
        {chat.map((c) =&amp;gt; (
          &amp;lt;&amp;gt;
            &amp;lt;Link href={`/chat/${c.id}`} className="flex flex-col gap-1"&amp;gt;
              &amp;lt;span className="text-sm overflow-hidden"&amp;gt;{c.id}&amp;lt;/span&amp;gt;
              &amp;lt;span&amp;gt;{c.createdAt.toDateString()}&amp;lt;/span&amp;gt;
            &amp;lt;/Link&amp;gt;
            &amp;lt;Separator className="my-2" /&amp;gt;
          &amp;lt;/&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/ScrollArea&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;src/db/schema.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  integer,
  sqliteTable,
  text,
  primaryKey,
} from "drizzle-orm/sqlite-core";
import type { AdapterAccount } from "next-auth/adapters";

export const users = sqliteTable("user", {
  id: text("id")
    .primaryKey()
    .$defaultFn(() =&amp;gt; crypto.randomUUID()),
  name: text("name"),
  email: text("email").notNull(),
  emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
  image: text("image"),
});

export const usersMessages = sqliteTable("userMessage", {
  id: text("id")
    .primaryKey()
    .$defaultFn(() =&amp;gt; crypto.randomUUID()),
  userId: text("userId")
    .notNull()
    .references(() =&amp;gt; users.id, { onDelete: "cascade" }),
  message: text("message").notNull(),
  createdAt: integer("createdAt", { mode: "timestamp_ms" }).notNull(),
});
export type UserMessage = typeof usersMessages.$inferInsert;

export const accounts = sqliteTable(
  "account",
  {
    userId: text("userId")
      .notNull()
      .references(() =&amp;gt; users.id, { onDelete: "cascade" }),
    type: text("type").$type&amp;lt;AdapterAccount["type"]&amp;gt;().notNull(),
    provider: text("provider").notNull(),
    providerAccountId: text("providerAccountId").notNull(),
    refresh_token: text("refresh_token"),
    access_token: text("access_token"),
    expires_at: integer("expires_at"),
    token_type: text("token_type"),
    scope: text("scope"),
    id_token: text("id_token"),
    session_state: text("session_state"),
  },
  (account) =&amp;gt; ({
    compoundKey: primaryKey({
      columns: [account.provider, account.providerAccountId],
    }),
  })
);

export const sessions = sqliteTable("session", {
  sessionToken: text("sessionToken").primaryKey(),
  userId: text("userId")
    .notNull()
    .references(() =&amp;gt; users.id, { onDelete: "cascade" }),
  expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
});

export const verificationTokens = sqliteTable(
  "verificationToken",
  {
    identifier: text("identifier").notNull(),
    token: text("token").notNull(),
    expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
  },
  (vt) =&amp;gt; ({
    compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
  })
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.env&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GENERATIVE_AI_API_KEY=

TURSO_CONNECTION_URL=
TURSO_AUTH_TOKEN=

AUTH_SECRET=

AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the completion of this tutorial, you've laid the foundation for an innovative spreadsheet application powered by CodeGPT. By integrating AI capabilities with Next.js, TypeScript, and Tailwind CSS, you're poised to create a versatile and efficient tool for data management and analysis. Embrace the possibilities of AI-driven development and continue exploring the limitless potential of CodeGPT in your projects. Happy coding!&lt;/p&gt;

&lt;p&gt;You can find the source code for this tutorial on GitHub:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rajanshresth" rel="noopener noreferrer"&gt;
        rajanshresth
      &lt;/a&gt; / &lt;a href="https://github.com/rajanshresth/code-gpt" rel="noopener noreferrer"&gt;
        code-gpt
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Code-GPT is an AI-powered web app that swiftly generates code snippets based on user descriptions, revolutionizing the coding experience with its intuitive interface and versatile language support.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;CodeGPT&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;CodeGPT is a revolutionary web application powered by AI, designed to streamline the coding experience. Generate code snippets swiftly and effortlessly with our intuitive interface and versatile language support.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Project URL&lt;/code&gt;: &lt;a href="https://code-gpt-rose.vercel.app/" rel="nofollow noopener noreferrer"&gt;CodeGPT Live&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;iframe width="560" height="315" src="&lt;a href="https://youtu.be/AWi7S624vCU" rel="nofollow noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://youtu.be/AWi7S624vCU" rel="noopener noreferrer"&gt;https://youtu.be/AWi7S624vCU&lt;/a&gt;" frameborder="0" allowfullscreen&amp;gt;&amp;lt;/iframe&amp;amp;gt&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;To get started with CodeGPT locally, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone this repository:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/rajanshresth/code-gpt.git&lt;/pre&gt;

&lt;/div&gt;

&lt;ol start="2"&gt;
&lt;li&gt;Navigate to the project directory:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; code-gpt&lt;/pre&gt;

&lt;/div&gt;

&lt;ol start="3"&gt;
&lt;li&gt;Install dependencies:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
yarn
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
pnpm install&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Run the development server:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm run dev
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
yarn dev
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
pnpm dev
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
bun dev&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="nofollow noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; with your browser to see the result.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Tech Stack&lt;/h2&gt;

&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;

&lt;strong&gt;Next.js:&lt;/strong&gt; &lt;a href="https://nextjs.org/" rel="nofollow noopener noreferrer"&gt;Next.js&lt;/a&gt; powers the frontend of CodeGPT, providing a fast and efficient web development experience.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Gemini:&lt;/strong&gt; We leverage Gemini for generative AI capabilities, enhancing CodeGPT's code generation capabilities.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;TypeScript:&lt;/strong&gt; TypeScript brings static typing to JavaScript, improving code quality and developer productivity.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Tailwind&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rajanshresth/code-gpt" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>ai</category>
      <category>programing</category>
    </item>
  </channel>
</rss>
