How to build: an AI-powered PowerPoint generator (Next.js, OpenAI, CopilotKit)
Tags: webdev, tutorial, programming, javascript
Published: 2024-02-20 15:30:56

#TL;DR

In this article, you will learn how to build an AI-powered PowerPoint application using Nextjs, CopilotKit & OpenAI. We will cover:

- Leveraging ChatGPT to create your PowerPoint presentation📊
- Chatting with your PowerPoint presentation💬
- Adding audio and images to your PowerPoint presentation🔉



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkbn3i2vw59na8yn2gm0.gif)

---

## CopilotKit: Build deeply integrated in-app AI chatbots 💬

CopilotKit is the [open-source AI copilot platform.](https://github.com/CopilotKit/CopilotKit) We make it easy to integrate powerful AI chatbots into your react apps. Fully customizable and deeply integrated. 





![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9wxu7q615z0nwo2111pr.gif)

{% cta https://github.com/CopilotKit/CopilotKit %} Star CopilotKit ⭐️ {% endcta %}



Now back to the article. 

---
## **Prerequisites**

To get started with this tutorial, you need the following installed on your computer:

- A text editor (e.g., Visual Studio Code)
- Node.js
- A package manager

## Creating The PowerPoint Application Frontend With NextJS

**Step 1:** Git clone the PowerPoint application boilerplate using the command below.

```jsx
git clone https://github.com/TheGreatBonnie/aipoweredpowerpointpresentation
```

**Step 2:** Open the PowerPoint application boilerplate on a text editor and install all project dependencies using the command below.

```jsx
npm install
```

Step 3: • Go to the root directory ****and create a file called **`.env.local`.** In the file, add the environment variable below that holds your  ChatGPT API key.

```jsx
OPENAI_API_KEY="Your ChatGPT API Key”
```

**Step 4:** Run the command *npm run dev* on the command line. Navigate to http://localhost:3000/, and you should see the PowerPoint application frontend.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kom1urw8ggeevz2dspk8.png)

## Creating PowerPoint Slides Functionality

Step 1: Go to **`/[root]/src/app/components`**, and create a file called `present.tsx`. Then import the following dependencies at the top of the file.

```tsx
"use client";

import { useCopilotContext } from "@copilotkit/react-core";
import { CopilotTask } from "@copilotkit/react-core";
import {
  useMakeCopilotActionable,
  useMakeCopilotReadable,
} from "@copilotkit/react-core";
import { useEffect, useState } from "react";
import "./../presentation/styles.css";
import Markdown from "react-markdown";
```

Step 2: Define a TypeScript interface called Slide with properties title and content for a PowerPoint presentation slide.

```
// Define slide interface
interface Slide {
  title: string;
  content: string;
  
}
```

Step 3: Create a function called `Presentation` and initialize state variables called `allSlides` and `currentSlideIndex` with `usestate.` Variable `allSlides` will hold an array of slides while `currentSlideIndex` will hold the current slide index.

```tsx
export function Presentation (){
  const [allSlides, setAllSlides] = useState<Slide[]>([]);
  const [currentSlideIndex, setCurrentSlideIndex] = useState<number>(0);
}
```

Step 4: Inside the `Presentation` function, use the `useMakeCopilotReadable` hook to add the `allSlides` array of slides as context for the in-app chatbot.

```jsx
useMakeCopilotReadable("Powerpoint presentation slides: " + JSON.stringify(allSlides));
```

Step 5: Use the `useMakeCopilotActionable` hook to set up an action called `createNewPowerPointSlide` with a description and an implementation function that updates the `allSlides` and `currentSlideIndex` state variables, as shown below.

```tsx

useMakeCopilotActionable(
        {
          name: "createNewPowerPointSlide",
          description: "create slides for a powerpoint presentation. Call this function multiple times to present multiple slides.",
          argumentAnnotations: [
            {
              name: "slideTitle",
              type: "string",
              description: "The topic to display in the presentation slide. Use simple markdown to outline your speech, like a headline.",
              required: true,
            },
            {
              name: "content",
              type: "string",
              description: "The content to display in the presentation slide. Use simple markdown to outline your speech, like lists, paragraphs, etc.",
              required: true
            },
           
          ],
    
          implementation: async (newSlideTitle, newSlideContent) => {
            const newSlide: Slide = { title: newSlideTitle, content: newSlideContent};
            const updatedSlides = [...allSlides, newSlide];
            setAllSlides(updatedSlides);
            setCurrentSlideIndex(updatedSlides.length - 1);
						
				
          },
        },
        [],
      );
```

Step 6: Define a function called `displayCurrentSlide` that displays the current slide index in the front end.

```tsx
// Display current slide
      const displayCurrentSlide = () => {
        const slide = allSlides[currentSlideIndex];
        return slide ? (
          
          <div
          className="h-screen flex flex-col justify-center items-center text-5xl text-white bg-cover bg-center bg-no-repeat p-10 text-center"
          style={{
              textShadow: "1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000",
            }}
          >
          <Markdown className="markdown">{slide.title}</Markdown>
          <Markdown className="markdown">{slide.content}</Markdown>
        </div>
        ) : (
          <div className="h-screen flex flex-col justify-center items-center text-5xl text-white bg-cover bg-center bg-no-repeat p-10 text-center">
            No Slide To Display
          </div>
        );
      };
```

Step 7: Define a function called addSlide that holds a class called CopilotTask. The CopilotTask class defines functionality for adding a new slide.

```tsx
// Add new slide function
  const addSlide = new CopilotTask({
    instructions: "create a new slide",
    actions: [
      {
        name: "newSlide",
        description: "Make a new slide related to the current topic.",
        argumentAnnotations: [
          {
            name: "title",
            type: "string",
            description: "The title to display in the presentation slide.",
            required: true,
          },
          {
            name: "content",
            type: "string",
            description: "The title to display in the presentation slide.",
            required: true,
          },
        ],

        implementation: async (newSlideTitle,newSlideContent,) => {
          const newSlide: Slide = {title: newSlideTitle,content: newSlideContent,};
          const updatedSlides = [...allSlides, newSlide];
          setAllSlides(updatedSlides);
          setCurrentSlideIndex(updatedSlides.length - 1);
        },
      },
    ],
  });

  const context = useCopilotContext();

  const [randomSlideTaskRunning, setRandomSlideTaskRunning] = useState(false);
```

Step 8: Define two functions called goBack and goForward. The goBack function defines functionality for navigating to the previous slide while the goForward function defines functionality for navigating to the next slide.

```tsx
// Button click handlers for navigation
      const goBack = () => setCurrentSlideIndex((prev) => Math.max(0, prev - 1));
      const goForward = () => setCurrentSlideIndex((prev) => Math.min(allSlides.length - 1, prev + 1));
```

Step 9: Return a component that calls displayCurrentSlide function and contains buttons that call addSlide, goBack and goForward functions.

```tsx
return (
        <div>
          {displayCurrentSlide()}
          <button
            disabled={randomSlideTaskRunning}
            className={`absolute bottom-12 left-0 mb-4 ml-4 bg-blue-500 text-white font-bold py-2 px-4 rounded
            ${randomSlideTaskRunning ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-700"}`}
            onClick={async () => {
              try {
                setRandomSlideTaskRunning(true);
                await addSlide.run(context);
              } finally {
                setRandomSlideTaskRunning(false);
              }
            }}
          >
            {randomSlideTaskRunning ? "Loading slide..." : "Add Slide +"}
          </button>
          <button 
          className={`absolute bottom-0 left-0 mb-4 ml-4 bg-blue-500 text-white font-bold py-2 px-4 rounded
          ${randomSlideTaskRunning ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-700"}`}
          onClick={goBack}>Prev</button>
          <button
          className={`absolute bottom-0 left-20 mb-4 ml-4 bg-blue-500 text-white font-bold py-2 px-4 rounded
          ${randomSlideTaskRunning ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-700"}`} 
          onClick={goForward}>Next</button>
        </div>
      );
```

Step 10: In the Presentation folder, add the following code to the page.tsx file.

```tsx
"use client";

import {
  CopilotKit
} from "@copilotkit/react-core";
import { CopilotSidebar } from "@copilotkit/react-ui";
import {Presentation} from "../components/present";
import "./styles.css";

let globalAudio: any = undefined;
let globalAudioEnabled = false;

const Demo = () => {
  return (
    <CopilotKit url="/api/copilotkit/openai">
      <CopilotSidebar
        defaultOpen={true}
        labels={{
          title: "Presentation Copilot",
          initial: "Hi you! 👋 I can give you a presentation on any topic.",
        }}
        clickOutsideToClose={false}
        onSubmitMessage={async (message) => {
          if (!globalAudioEnabled) {
            globalAudio.play();
            globalAudio.pause();
          }
          globalAudioEnabled = true;
        }}
      >
        <Presentation/>
      </CopilotSidebar>
    </CopilotKit>
  );
};

export default Demo;
```

Step 11: Navigate to http://localhost:3000/, click the ‘Get Started’ button, and you will be redirected to the presentation page that is integrated with a chatbot, as shown below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a74ilgzf5ep6snbli7uh.png)

Step 12: Give the chatbot on the right side a prompt like “Create a PowerPoint presentation on TypeScript” The chatbot will start generating a response, and once it is done, it will display the generated PowerPoint slide on the left side of the page, as shown belo


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxhkvltf33uwyvrt4a8a.png)


Step 13: Close the chatbot window and click the Add Slide + button to add a new slide to the PowerPoint presentation as shown below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kxl59gxkt02pmyadxzuk.png)

Step 14: Click the Prev button and you will navigate to the previous slide. If you click the Next button, you will navigate to the next slide.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/expdwslfo2o49x7y6a6p.png)

## Creating PowerPoint Slides Auto Speech Functionality

Step 1: In the `present.tsx` file, declare a variable called `globalAudio`, as shown below.

```jsx
let globalAudio: any = undefined;
```

Step 2: Inside the `Presentation` component, declare a `useEffect` hook that initializes **`globalAudio`** with a new **`Audio`** object, as shown below.

```tsx
useEffect(() => {
        if (!globalAudio) {
          globalAudio = new Audio();
        }
      }, []);
```

Step 3: Update the useMakeCopilotActionable hook to convert PowerPoint slides text into speech via an API, as shown below.

```tsx

useMakeCopilotActionable(
        {
          name: "createNewPowerPointSlide",
          description: "create slides for a powerpoint presentation. Call this function multiple times to present multiple slides.",
          argumentAnnotations: [
            {
              name: "slideTitle",
              type: "string",
              description: "The topic to display in the presentation slide. Use simple markdown to outline your speech, like a headline.",
              required: true,
            },
            {
              name: "content",
              type: "string",
              description: "The content to display in the presentation slide. Use simple markdown to outline your speech, like lists, paragraphs, etc.",
              required: true
            },
            
            {
                name: "speech",
                type: "string",
                description: "An informative speech about the current slide.",
                required: true,
            },
          ],
    
          implementation: async (newSlideTitle, newSlideContent, speech) => {
            const newSlide: Slide = { title: newSlideTitle, content: newSlideContent };
            const updatedSlides = [...allSlides, newSlide];
            setAllSlides(updatedSlides);
            setCurrentSlideIndex(updatedSlides.length - 1);

            const encodedText = encodeURIComponent(speech);
            const url = `/api/tts?text=${encodedText}`;
            globalAudio.src = url;
            await globalAudio.play();
            await new Promise<void>((resolve) => {
                globalAudio.onended = function () {
                resolve();
                };
            });
            await new Promise((resolve) => setTimeout(resolve, 500));
          },
        },
        [],
      );
```

Step 4: Update the addSlide function to convert new PowerPoint slides text into speech via an API, as shown below.

```tsx
// Add new slide function
      const addSlide = new CopilotTask({
        instructions: "create a new slide",
        actions: [
          {
          name: "newSlide",
          description: "Make a new slide related to the current topic.",
          argumentAnnotations: [
            {
              name: "title",
              type: "string",
              description:"The title to display in the presentation slide.",
              required: true,
            },  
            {
              name: "content",
              type: "string",
              description:"The title to display in the presentation slide.",
              required: true,
            },
           
            {
                name: "speech",
                type: "string",
                description: "An informative speech about the current slide.",
                required: true,
            },     
          ],
      
          implementation: async (newSlideTitle, newSlideContent, speech) => {
            const newSlide: Slide = { title: newSlideTitle, content: newSlideContent };
            const updatedSlides = [...allSlides, newSlide];
            setAllSlides(updatedSlides);
            setCurrentSlideIndex(updatedSlides.length - 1);

            const encodedText = encodeURIComponent(speech);
            const url = `/api/tts?text=${encodedText}`;
            globalAudio.src = url;
            await globalAudio.play();
            await new Promise<void>((resolve) => {
                globalAudio.onended = function () {
                resolve();
                };
            });
            await new Promise((resolve) => setTimeout(resolve, 500));
          },
        }
        ]
      });
```

Step 5: Give the chatbot the “Create a PowerPoint presentation on TypeScript” prompt again and you should hear a voice presenting the slides.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iu6s1c4q5foto1xe919a.png)

## Creating Image Generation Functionality

Step 1: In the present.tsx file, add a new property called backgroundImage to type the interface Slide as shown below.

```tsx
// Define slide interface
interface Slide {
  title: string;
  content: string;
  backgroundImage: string;
}
```

Step 2: Update the useMakeCopilotActionable hook to generate images for PowerPoint presentation slides.

```tsx

useMakeCopilotActionable(
        {
          name: "createPowerPointSlides",
          description: "create slides for a powerpoint presentation. Call this function multiple times to present multiple slides.",
          argumentAnnotations: [
            {
              name: "slideTitle",
              type: "string",
              description: "The topic to display in the presentation slide. Use simple markdown to outline your speech, like a headline.",
              required: true,
            },
            {
              name: "content",
              type: "string",
              description: "The content to display in the presentation slide. Use simple markdown to outline your speech, like lists, paragraphs, etc.",
              required: true
            },
            {
                name: "backgroundImage",
                type: "string",
                description: "What to display in the background of the slide (i.e. 'dog' or 'house').",
                required: true,
              },
            {
                name: "speech",
                type: "string",
                description: "An informative speech about the current slide.",
                required: true,
            },
          ],
    
          implementation: async (newSlideTitle, newSlideContent, newSlideBackgroundImage, speech) => {
            const newSlide: Slide = { title: newSlideTitle, content: newSlideContent, backgroundImage: newSlideBackgroundImage };
            const updatedSlides = [...allSlides, newSlide];
            setAllSlides(updatedSlides);
            setCurrentSlideIndex(updatedSlides.length - 1);

            const encodedText = encodeURIComponent(speech);
            const url = `/api/tts?text=${encodedText}`;
            globalAudio.src = url;
            await globalAudio.play();
            await new Promise<void>((resolve) => {
                globalAudio.onended = function () {
                resolve();
                };
            });
            await new Promise((resolve) => setTimeout(resolve, 500));
          },
        },
        [],
      );
```

Step 2: Update the addSlide function to generate images for new PowerPoint presentation slides.

**Step 3:** Update the `Slide` component in the `slide.tsx` file to generate an image via [`unsplash.com`](http://unsplash.com/) 

```tsx
// Add new slide function
      const addSlide = new CopilotTask({
        instructions: "create a new slide",
        functions: [
          {
          name: "newSlide",
          description: "Make a new slide related to the current topic.",
          argumentAnnotations: [
            {
              name: "title",
              type: "string",
              description:"The title to display in the presentation slide.",
              required: true,
            },  
            {
              name: "content",
              type: "string",
              description:"The title to display in the presentation slide.",
              required: true,
            },
            {
                name: "backgroundImage",
                type: "string",
                description: "What to display in the background of the slide (i.e. 'dog' or 'house').",
                required: true,
              },
            {
                name: "speech",
                type: "string",
                description: "An informative speech about the current slide.",
                required: true,
            },     
          ],
      
          implementation: async (newSlideTitle, newSlideContent, newSlideBackgroundImage, speech) => {
            const newSlide: Slide = { title: newSlideTitle, content: newSlideContent, backgroundImage: newSlideBackgroundImage };
            const updatedSlides = [...allSlides, newSlide];
            setAllSlides(updatedSlides);
            setCurrentSlideIndex(updatedSlides.length - 1);

            const encodedText = encodeURIComponent(speech);
            const url = `/api/tts?text=${encodedText}`;
            globalAudio.src = url;
            await globalAudio.play();
            await new Promise<void>((resolve) => {
                globalAudio.onended = function () {
                resolve();
                };
            });
            await new Promise((resolve) => setTimeout(resolve, 500));
          },
        }
        ]
      });
```

Step 3: Update the displayCurrentSlide function to add a background image to the PowerPoint presentation slides.

```tsx
// Display current slide
      const displayCurrentSlide = () => {
        const slide = allSlides[currentSlideIndex];
        return slide ? (
          
          <div
          className="h-screen flex flex-col justify-center items-center text-5xl text-white bg-cover bg-center bg-no-repeat p-10 text-center"
          style={{
              backgroundImage: 'url("https://source.unsplash.com/featured/?' + encodeURIComponent(allSlides[currentSlideIndex].backgroundImage) + '")',
              textShadow: "1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000",
            }}
          >
          <Markdown className="markdown">{slide.title}</Markdown>
          <Markdown className="markdown">{slide.content}</Markdown>
        </div>
        ) : (
          <div className="h-screen flex flex-col justify-center items-center text-5xl text-white bg-cover bg-center bg-no-repeat p-10 text-center">
            No Slide To Display
          </div>
        );
      };
```

**Step 4:** Go to the web app and you should see a background image is added to the PowerPoint slide.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zgcgn1h6c5hz8dh7m9a.png)

## Conclusion

In conclusion, you can use CopilotKit to build in-app AI chatbots that can see the current app state and take action inside your app. The AI chatbot can talk to your app frontend, backend, and third-party services.


## Conclusion

In conclusion, you can use CopilotKit to build in-app AI chatbots that can see the current app state and take action inside your app. The AI chatbot can talk to your app frontend, backend, and third-party services.


For the full source-code: 
https://github.com/TheGreatBonnie/aipoweredpresentation

Thanks to @theGreatBonnie for his awesome work on this article. 

Don't forget to...

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pixiay2v8raimvui28l6.gif)


{% cta https://github.com/CopilotKit/CopilotKit %} Star CopilotKit ⭐️ {% endcta %}