├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── next.svg
├── run-demo-cta.png
├── screenshot.png
└── vercel.svg
├── src
├── app
│ ├── api
│ │ └── copilotkit
│ │ │ └── route.ts
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── components
│ └── TodoItem.tsx
└── types
│ └── todo.ts
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a demo that showcases using CopilotKit to build a simple Todo app.
2 |
3 | ## Run the live demo
4 |
5 | Want to see CopilotKit in action? Click the button below to try the live demo.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ## Deploy with Vercel
16 |
17 | To deploy with Vercel, click the button below:
18 |
19 | [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FCopilotKit%2Fdemo-todo&env=NEXT_PUBLIC_COPILOT_CLOUD_API_KEY&project-name=copilotkit-demo-todo&repository-name=copilotkit-demo-todo)
20 |
21 | ## How to Build: a To-Do list app with an embedded AI copilot
22 |
23 | Learn how to create a To-Do list app with an embedded AI copilot. This tutorial will guide you through the process step-by-step.
24 |
25 | Tutorial: [How to Build: a To-Do list app with an embedded AI copilot](https://dev.to/copilotkit/how-to-build-an-ai-powered-to-do-list-nextjs-gpt4-copilotkit-20i4)
26 |
27 | ## Add your OpenAI API key
28 |
29 | Add your environment variables to `.env.local` in the root of the project.
30 |
31 | ```
32 | OPENAI_API_KEY=your-api-key
33 | ```
34 |
35 | ## Install dependencies
36 |
37 | ```bash
38 | npm install
39 | ```
40 |
41 | ## Run the development server
42 |
43 | ```bash
44 | npm run dev
45 | ```
46 |
47 | ## Open the demo
48 |
49 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
50 |
51 | ## The Copilot-Specific parts of the code:
52 |
53 | 1. Notice `` and `` in `page.tsx`
54 |
55 | 2. Notice `useCopilotReadable` in `page.tsx`
56 |
57 | 3. Notice the 2 `useCopilotAction` in `page.tsx`
58 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@copilotkit/runtime": "1.0.0-beta.2",
13 | "@copilotkit/react-core": "1.0.0-beta.2",
14 | "@copilotkit/react-textarea": "1.0.0-beta.2",
15 | "@copilotkit/react-ui": "1.0.0-beta.2",
16 | "@copilotkit/shared": "1.0.0-beta.2",
17 | "nanoid": "^5.0.6",
18 | "next": "14.1.3",
19 | "react": "^18",
20 | "react-dom": "^18"
21 | },
22 | "devDependencies": {
23 | "@types/node": "^20",
24 | "@types/react": "^18",
25 | "@types/react-dom": "^18",
26 | "autoprefixer": "^10.0.1",
27 | "eslint": "^8",
28 | "eslint-config-next": "14.1.3",
29 | "postcss": "^8",
30 | "tailwindcss": "^3.3.0",
31 | "typescript": "^5"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/run-demo-cta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CopilotKit/demo-todo/da61e571c8c569e2c585ac548eb5b8129e3f17a6/public/run-demo-cta.png
--------------------------------------------------------------------------------
/public/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CopilotKit/demo-todo/da61e571c8c569e2c585ac548eb5b8129e3f17a6/public/screenshot.png
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/api/copilotkit/route.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest } from "next/server";
2 | import {
3 | CopilotRuntime,
4 | OpenAIAdapter,
5 | copilotRuntimeNextJSAppRouterEndpoint,
6 | } from "@copilotkit/runtime";
7 |
8 | export const POST = async (req: NextRequest) => {
9 | const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
10 | runtime: new CopilotRuntime(),
11 | serviceAdapter: new OpenAIAdapter(),
12 | endpoint: req.nextUrl.pathname,
13 | });
14 |
15 | return handleRequest(req);
16 | };
17 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CopilotKit/demo-todo/da61e571c8c569e2c585ac548eb5b8129e3f17a6/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 |
{children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { TodoItem } from "@/components/TodoItem";
4 | import { nanoid } from "nanoid";
5 | import { useState } from "react";
6 | import { Todo } from "../types/todo";
7 |
8 | /**
9 | *
10 | * 1) CopilotKit Integration
11 | *
12 | **/
13 |
14 | import {
15 | CopilotKit,
16 | useCopilotAction,
17 | useCopilotReadable,
18 | } from "@copilotkit/react-core";
19 | import { CopilotPopup } from "@copilotkit/react-ui";
20 | import "@copilotkit/react-ui/styles.css";
21 |
22 | export default function Home() {
23 | return (
24 |
25 |
Hello CopilotKit 🪁
26 | Todo List Example
27 |
28 | {/**
29 | *
30 | * 2) Wrap the TodoList component with CopilotKit
31 | *
32 | **/}
33 |
34 |
39 |
40 |
41 | {/**
42 | *
43 | * 3) Add the CopilotPopup component to get the chat
44 | *
45 | */}
46 |
47 |
59 |
60 |
61 | );
62 | }
63 |
64 | const TodoList: React.FC = () => {
65 | const [todos, setTodos] = useState([]);
66 | const [input, setInput] = useState("");
67 |
68 | /**
69 | *
70 | * 4) make the users todo list available with useCopilotReadable
71 | *
72 | **/
73 | useCopilotReadable({
74 | description: "The user's todo list.",
75 | value: todos,
76 | });
77 |
78 | /**
79 | *
80 | * 5) Add the useCopilotAction to enable the copilot to interact with the todo list
81 | *
82 | **/
83 |
84 | useCopilotAction({
85 | name: "updateTodoList",
86 | description: "Update the users todo list",
87 | parameters: [
88 | {
89 | name: "items",
90 | type: "object[]",
91 | description: "The new and updated todo list items.",
92 | attributes: [
93 | {
94 | name: "id",
95 | type: "string",
96 | description:
97 | "The id of the todo item. When creating a new todo item, just make up a new id.",
98 | },
99 | {
100 | name: "text",
101 | type: "string",
102 | description: "The text of the todo item.",
103 | },
104 | {
105 | name: "isCompleted",
106 | type: "boolean",
107 | description: "The completion status of the todo item.",
108 | },
109 | {
110 | name: "assignedTo",
111 | type: "string",
112 | description:
113 | "The person assigned to the todo item. If you don't know, assign it to 'YOU'.",
114 | required: true,
115 | },
116 | ],
117 | },
118 | ],
119 | handler: ({ items }) => {
120 | console.log(items);
121 | const newTodos = [...todos];
122 | for (const item of items) {
123 | const existingItemIndex = newTodos.findIndex(
124 | (todo) => todo.id === item.id
125 | );
126 | if (existingItemIndex !== -1) {
127 | newTodos[existingItemIndex] = item;
128 | } else {
129 | newTodos.push(item);
130 | }
131 | }
132 | setTodos(newTodos);
133 | },
134 | render: "Updating the todo list...",
135 | });
136 |
137 | /**
138 | *
139 | * 5) Add another useCopilotAction to enable the copilot to delete a todo item
140 | *
141 | **/
142 | useCopilotAction({
143 | name: "deleteTodo",
144 | description: "Delete a todo item",
145 | parameters: [
146 | {
147 | name: "id",
148 | type: "string",
149 | description: "The id of the todo item to delete.",
150 | },
151 | ],
152 | handler: ({ id }) => {
153 | setTodos(todos.filter((todo) => todo.id !== id));
154 | },
155 | render: "Deleting a todo item...",
156 | });
157 |
158 | const addTodo = () => {
159 | if (input.trim() !== "") {
160 | // Check if input is not just whitespace
161 | const newTodo: Todo = {
162 | id: nanoid(),
163 | text: input.trim(), // Trim whitespace
164 | isCompleted: false,
165 | };
166 | setTodos([...todos, newTodo]);
167 | setInput(""); // Reset input field
168 | }
169 | };
170 |
171 | const handleKeyPress = (e: React.KeyboardEvent) => {
172 | if (e.key === "Enter") {
173 | addTodo();
174 | }
175 | };
176 |
177 | const toggleComplete = (id: string) => {
178 | setTodos(
179 | todos.map((todo) =>
180 | todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : todo
181 | )
182 | );
183 | };
184 |
185 | const deleteTodo = (id: string) => {
186 | setTodos(todos.filter((todo) => todo.id !== id));
187 | };
188 |
189 | const assignPerson = (id: string, person: string | null) => {
190 | setTodos(
191 | todos.map((todo) =>
192 | todo.id === id
193 | ? { ...todo, assignedTo: person ? person : undefined }
194 | : todo
195 | )
196 | );
197 | };
198 |
199 | return (
200 |
201 |
202 | setInput(e.target.value)}
206 | onKeyDown={handleKeyPress} // Add this to handle the Enter key press
207 | />
208 |
214 |
215 | {todos.length > 0 && (
216 |
217 | {todos.map((todo, index) => (
218 |
226 | ))}
227 |
228 | )}
229 |
230 | );
231 | };
232 |
--------------------------------------------------------------------------------
/src/components/TodoItem.tsx:
--------------------------------------------------------------------------------
1 | import { Todo } from "@/types/todo";
2 |
3 | interface TodoItemProps {
4 | todo: Todo;
5 | toggleComplete: (id: string) => void;
6 | deleteTodo: (id: string) => void;
7 | assignPerson: (id: string, person: string | null) => void;
8 | hasBorder?: boolean;
9 | }
10 |
11 | export const TodoItem: React.FC = ({
12 | todo,
13 | toggleComplete,
14 | deleteTodo,
15 | assignPerson,
16 | hasBorder,
17 | }) => {
18 | return (
19 |
25 |
26 | toggleComplete(todo.id)}
31 | />
32 |
37 | {todo.assignedTo && (
38 |
39 | {todo.assignedTo}
40 |
41 | )}
42 | {todo.text}
43 |
44 |
45 |
46 |
65 |
87 |
88 |
89 | );
90 | };
91 |
--------------------------------------------------------------------------------
/src/types/todo.ts:
--------------------------------------------------------------------------------
1 | export interface Todo {
2 | id: string;
3 | text: string;
4 | isCompleted: boolean;
5 | assignedTo?: string;
6 | }
7 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------