DEV Community

Cover image for How to build: an AI-powered PowerPoint generator (Next.js, OpenAI, CopilotKit)
uliyahoo Subscriber for CopilotKit

Posted on • Updated on

How to build: an AI-powered PowerPoint generator (Next.js, OpenAI, CopilotKit)

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


CopilotKit: Build deeply integrated in-app AI chatbots 💬

CopilotKit is the open-source AI copilot platform. We make it easy to integrate powerful AI chatbots into your react apps. Fully customizable and deeply integrated.

Image description

Star CopilotKit ⭐️

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.



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


Enter fullscreen mode Exit fullscreen mode

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



npm install


Enter fullscreen mode Exit fullscreen mode

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.



OPENAI_API_KEY="Your ChatGPT API Key”


Enter fullscreen mode Exit fullscreen mode

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

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.



"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";


Enter fullscreen mode Exit fullscreen mode

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;

}


Enter fullscreen mode Exit fullscreen mode

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.



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


Enter fullscreen mode Exit fullscreen mode

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



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


Enter fullscreen mode Exit fullscreen mode

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.




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);


          },
        },
        [],
      );


Enter fullscreen mode Exit fullscreen mode

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



// 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>
        );
      };


Enter fullscreen mode Exit fullscreen mode

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



// 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);


Enter fullscreen mode Exit fullscreen mode

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.



// 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));


Enter fullscreen mode Exit fullscreen mode

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



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>
      );


Enter fullscreen mode Exit fullscreen mode

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



"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;


Enter fullscreen mode Exit fullscreen mode

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

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

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

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

Creating PowerPoint Slides Auto Speech Functionality

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



let globalAudio: any = undefined;


Enter fullscreen mode Exit fullscreen mode

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



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


Enter fullscreen mode Exit fullscreen mode

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




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));
          },
        },
        [],
      );


Enter fullscreen mode Exit fullscreen mode

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



// 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));
          },
        }
        ]
      });


Enter fullscreen mode Exit fullscreen mode

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

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.



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


Enter fullscreen mode Exit fullscreen mode

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




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));
          },
        },
        [],
      );


Enter fullscreen mode Exit fullscreen mode

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



// 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));
          },
        }
        ]
      });


Enter fullscreen mode Exit fullscreen mode

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



// 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>
        );
      };


Enter fullscreen mode Exit fullscreen mode

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

Image description

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

Star CopilotKit ⭐️

Top comments (14)

Collapse
 
nevodavid profile image
Nevo David

Great article!
I wonder if it's possible to use also dalle 2 or midjourney to generate the slides pictures

Collapse
 
uliyahoo profile image
uliyahoo

Yup. In the latest version of the demo it is doing that.

Collapse
 
envitab profile image
Ekemini Samuel

Great article!

Another learning experience for me, thanks for sharing. I'm looking forward to the next one.

Collapse
 
uliyahoo profile image
uliyahoo

Thanks for checking it out!

Collapse
 
jeremiah_the_dev_man profile image
Jeremiah

Cool!

I liked your last article. Looking forward to going through this one this weekend!

Collapse
 
uliyahoo profile image
uliyahoo

Thanks!

We worked really hard on this one and I think it's very cool so I hope you enjoy!

Collapse
 
johny0012 profile image
Johny

Yup, definitely a weekend kind of project...

Collapse
 
matijasos profile image
Matija Sosic

Nice! Is CopilotKit open source? It might be a great fit for Open SaaS (an open-source, free alternative to $300+ boilerplate starters) then
github.com/wasp-lang/open-saas/

Collapse
 
uliyahoo profile image
uliyahoo • Edited

There is a lot of natural fit between open SaaS and CopilotKit. Let's talk!

Collapse
 
david-723 profile image
David

Can you alter the presentations after the AI made them?

Collapse
 
steven0121 profile image
Steven

It seems like you can through the chat...

Collapse
 
uliyahoo profile image
uliyahoo

Yeah exactly ^

Collapse
 
johny0012 profile image
Johny

Sounds like a cool project. Kind of a long read though, so saved it for later.

Collapse
 
uliyahoo profile image
uliyahoo

Hope you enjoyed!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.