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

·

10 min read

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🔉

CopilotKit: Build deeply integrated in-app AI chatbots 💬

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

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

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

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.

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.

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

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.

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.

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.

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.

// 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.

// 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.

// 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 the displayCurrentSlide function and contains buttons that call the 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>
      );

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;

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.

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 below.

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

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.

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;

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

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

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

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

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

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

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

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

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

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

Don’t forget to… Star CopilotKit ⭐️