ChenyuRabbitLove commited on
Commit
f6a1fd1
·
1 Parent(s): 590171e

first commit

Browse files
.eslintrc.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "extends": ["next/core-web-vitals", "next/typescript"]
3
+ }
.gitattributes copy ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+
32
+ # env files (can opt-in for commiting if needed)
33
+ .env*
34
+
35
+ # vercel
36
+ .vercel
37
+
38
+ # typescript
39
+ *.tsbuildinfo
40
+ next-env.d.ts
41
+
42
+ # Python
43
+ __pycache__/
44
+ *.py[cod]
45
+ *$py.class
46
+ .Python
47
+ env/
48
+ venv/
49
+ .env
50
+ node_modules
Dockerfile ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-alpine AS base
2
+
3
+ # Frontend build
4
+ FROM base AS frontend-deps
5
+ RUN apk add --no-cache libc6-compat
6
+ WORKDIR /app
7
+ COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
8
+ RUN \
9
+ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
10
+ elif [ -f package-lock.json ]; then npm ci; \
11
+ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
12
+ else echo "Lockfile not found." && exit 1; \
13
+ fi
14
+
15
+ FROM base AS frontend-builder
16
+ WORKDIR /app
17
+ COPY --from=frontend-deps /app/node_modules ./node_modules
18
+ COPY . .
19
+ RUN npm run build
20
+
21
+ # Backend build
22
+ FROM python:3.9-slim AS backend
23
+ WORKDIR /backend
24
+ COPY backend/requirements.txt .
25
+ RUN pip install --no-cache-dir -r requirements.txt
26
+ COPY backend .
27
+
28
+ # Final image
29
+ FROM python:3.9-slim AS runner
30
+ WORKDIR /app
31
+
32
+ ENV NODE_ENV production
33
+
34
+ # Install Node.js and wget
35
+ RUN apt-get update && apt-get install -y curl wget && \
36
+ curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
37
+ apt-get install -y nodejs && \
38
+ apt-get clean && \
39
+ rm -rf /var/lib/apt/lists/*
40
+
41
+ RUN addgroup --system --gid 1001 nodejs && \
42
+ adduser --system --uid 1001 nextjs
43
+
44
+ COPY --from=frontend-builder /app/public ./frontend/public
45
+ COPY --from=frontend-builder --chown=nextjs:nodejs /app/.next/standalone ./frontend
46
+ COPY --from=frontend-builder --chown=nextjs:nodejs /app/.next/static ./frontend/.next/static
47
+ COPY --from=frontend-builder --chown=nextjs:nodejs /app/.next/cache ./frontend/.next/cache
48
+
49
+ COPY --from=backend /backend ./backend
50
+ COPY --from=backend /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
51
+
52
+ COPY start.sh .
53
+ RUN chmod +x start.sh
54
+
55
+ EXPOSE 3000 8000
56
+
57
+ CMD ["./start.sh"]
README copy.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Playgo Next
3
+ emoji: 🐢
4
+ colorFrom: pink
5
+ colorTo: yellow
6
+ sdk: docker
7
+ app_port: 3000
8
+ pinned: false
9
+ ---
app/components/ChatBot.tsx ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { useState, useRef, useEffect } from 'react'
4
+ import { useChat } from 'ai/react'
5
+
6
+ type MessageWithLoading = {
7
+ content: string;
8
+ role: string;
9
+ isStreaming?: boolean;
10
+ }
11
+
12
+ const EXAMPLE_PROMPTS = [
13
+ {
14
+ title: "Personalized Learning",
15
+ description: "Get tailored explanations",
16
+ prompt: "Can you help me understand photosynthesis in a simple way?"
17
+ },
18
+ {
19
+ title: "24/7 Availability",
20
+ description: "Learn at your own pace",
21
+ prompt: "I need help solving this quadratic equation: x² + 5x + 6 = 0"
22
+ },
23
+ {
24
+ title: "Multiple Subjects",
25
+ description: "From math to literature",
26
+ prompt: "What are the main themes in Shakespeare's Macbeth?"
27
+ }
28
+ ]
29
+
30
+ export default function LandingPageChatBot() {
31
+ const { messages: rawMessages, input, handleInputChange, handleSubmit, isLoading, append } = useChat({
32
+ api: '/api/landing_page_chat',
33
+ streamProtocol: 'data',
34
+ onError: (error) => {
35
+ console.error('Chat error:', error);
36
+ },
37
+ onFinish: (message) => {
38
+ console.log('Chat finished:', message);
39
+ setMessages(prev => prev.map(msg => ({...msg, isStreaming: false})))
40
+ },
41
+ keepLastMessageOnError: true,
42
+ })
43
+
44
+ const chatContainerRef = useRef<HTMLDivElement>(null)
45
+ const [hasInteracted, setHasInteracted] = useState(false)
46
+ const [messages, setMessages] = useState<MessageWithLoading[]>([])
47
+
48
+ useEffect(() => {
49
+ setMessages(rawMessages.map((msg, index) => ({
50
+ ...msg,
51
+ isStreaming: isLoading && index === rawMessages.length - 1 && msg.role === 'assistant'
52
+ })))
53
+ }, [rawMessages, isLoading])
54
+
55
+ const scrollToBottom = () => {
56
+ if (chatContainerRef.current) {
57
+ const { scrollHeight, clientHeight } = chatContainerRef.current
58
+ chatContainerRef.current.scrollTop = scrollHeight - clientHeight
59
+ }
60
+ }
61
+
62
+ useEffect(() => {
63
+ scrollToBottom()
64
+ }, [messages])
65
+
66
+ const sendMessage = async (text: string) => {
67
+ setHasInteracted(true)
68
+ await append({
69
+ content: text,
70
+ role: 'user',
71
+ })
72
+ }
73
+
74
+ const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
75
+ setHasInteracted(true)
76
+ handleSubmit(e)
77
+ }
78
+
79
+ return (
80
+ <section className="w-full h-[800px] bg-gradient-to-b from-background-secondary to-background-primary flex items-center justify-center px-4">
81
+ <div className="w-full max-w-5xl mx-auto h-full flex items-center">
82
+ {!hasInteracted && messages.length === 0 ? (
83
+ <div className="text-center space-y-8 w-full">
84
+ <div className="space-y-4">
85
+ <h2 className="text-5xl font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
86
+ AI Learning Assistant
87
+ </h2>
88
+ <p className="text-text-secondary text-xl max-w-2xl mx-auto leading-relaxed">
89
+ Explore new topics, get homework help, or discover learning resources
90
+ </p>
91
+ </div>
92
+
93
+ <form onSubmit={onSubmit} className="max-w-3xl mx-auto">
94
+ <div className="relative flex items-center">
95
+ <input
96
+ type="text"
97
+ value={input}
98
+ onChange={handleInputChange}
99
+ placeholder="Ask anything about learning..."
100
+ className="w-full p-6 pr-36 rounded-2xl border border-border/50 bg-background-primary/50
101
+ backdrop-blur-sm shadow-lg focus:outline-none focus:ring-2 focus:ring-primary/50
102
+ text-lg transition-all duration-200 placeholder:text-text-secondary/50"
103
+ />
104
+ <button
105
+ type="submit"
106
+ disabled={isLoading}
107
+ className="absolute right-2 p-3 bg-primary text-white rounded-full
108
+ hover:bg-primary/90 transition-all duration-200
109
+ shadow-md hover:shadow-lg active:scale-95 disabled:opacity-50
110
+ disabled:hover:bg-primary disabled:cursor-not-allowed"
111
+ >
112
+ <svg
113
+ xmlns="http://www.w3.org/2000/svg"
114
+ fill="none"
115
+ viewBox="0 0 24 24"
116
+ strokeWidth="1.5"
117
+ stroke="currentColor"
118
+ className={`size-6 ${isLoading ? 'animate-pulse' : ''}`}
119
+ >
120
+ <path
121
+ strokeLinecap="round"
122
+ strokeLinejoin="round"
123
+ d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5"
124
+ />
125
+ </svg>
126
+ </button>
127
+ </div>
128
+ </form>
129
+
130
+ <div className="mt-12">
131
+ <h3 className="text-xl font-semibold text-text-primary mb-6">Try these examples:</h3>
132
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
133
+ {EXAMPLE_PROMPTS.map((prompt, index) => (
134
+ <button
135
+ key={index}
136
+ onClick={() => sendMessage(prompt.prompt)}
137
+ className="group p-6 rounded-2xl border border-border/50 bg-background-primary/30
138
+ hover:bg-background-primary/50 backdrop-blur-sm transition-all duration-200
139
+ hover:border-primary/50 hover:shadow-lg text-left"
140
+ >
141
+ <div className="flex items-center justify-between mb-2">
142
+ <h4 className="font-semibold text-text-primary">{prompt.title}</h4>
143
+ <svg
144
+ className="w-5 h-5 text-primary opacity-0 group-hover:opacity-100 transition-opacity duration-200"
145
+ fill="none"
146
+ stroke="currentColor"
147
+ viewBox="0 0 24 24"
148
+ >
149
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
150
+ </svg>
151
+ </div>
152
+ <p className="text-text-secondary text-sm mb-2">{prompt.description}</p>
153
+ <p className="text-primary text-sm font-medium truncate">{prompt.prompt}</p>
154
+ </button>
155
+ ))}
156
+ </div>
157
+ </div>
158
+ </div>
159
+ ) : (
160
+ <div className="bg-background-primary/50 backdrop-blur-sm rounded-2xl shadow-lg border border-border/50 overflow-hidden w-full h-[700px] flex flex-col">
161
+ <div className="p-6 border-b border-border/50 bg-background-secondary/30">
162
+ <h2 className="text-xl font-semibold text-text-primary">AI Learning Assistant</h2>
163
+ </div>
164
+
165
+ <div
166
+ ref={chatContainerRef}
167
+ className="flex-1 overflow-y-auto p-6 space-y-6"
168
+ >
169
+ {messages.map((message, index) => (
170
+ <div
171
+ key={index}
172
+ className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
173
+ >
174
+ <div
175
+ className={`max-w-[80%] p-4 rounded-2xl shadow-sm ${
176
+ message.role === 'user'
177
+ ? 'bg-primary text-white rounded-br-none'
178
+ : 'bg-background-secondary/50 text-text-primary rounded-bl-none'
179
+ }`}
180
+ >
181
+ {message.content}
182
+ {message.isStreaming && (
183
+ <span className="inline-block w-2.5 h-2.5 ml-1.5 bg-primary rounded-full animate-pulse" />
184
+ )}
185
+ </div>
186
+ </div>
187
+ ))}
188
+ </div>
189
+
190
+ <div className="p-6 border-t border-border/50 bg-background-secondary/30">
191
+ <form onSubmit={onSubmit}>
192
+ <div className="relative flex items-center">
193
+ <input
194
+ type="text"
195
+ value={input}
196
+ onChange={handleInputChange}
197
+ placeholder={isLoading ? "AI is thinking..." : "Type your message..."}
198
+ disabled={isLoading}
199
+ className="w-full p-4 pr-32 rounded-xl border border-border/50 bg-background-primary/50
200
+ backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-primary/50
201
+ text-base transition-all duration-200 disabled:opacity-50"
202
+ />
203
+ <button
204
+ type="submit"
205
+ disabled={isLoading}
206
+ className="absolute right-2 p-2 bg-primary text-white rounded-full
207
+ hover:bg-primary/90 transition-all duration-200
208
+ shadow-md hover:shadow-lg active:scale-95 disabled:opacity-50
209
+ disabled:hover:bg-primary disabled:cursor-not-allowed"
210
+ >
211
+ <svg
212
+ xmlns="http://www.w3.org/2000/svg"
213
+ fill="none"
214
+ viewBox="0 0 24 24"
215
+ strokeWidth="1.5"
216
+ stroke="currentColor"
217
+ className={`size-5 ${isLoading ? 'animate-pulse' : ''}`}
218
+ >
219
+ <path
220
+ strokeLinecap="round"
221
+ strokeLinejoin="round"
222
+ d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5"
223
+ />
224
+ </svg>
225
+ </button>
226
+ </div>
227
+ </form>
228
+ </div>
229
+ </div>
230
+ )}
231
+ </div>
232
+ </section>
233
+ )
234
+ }
app/components/ThemeProvider.tsx ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { createContext, useContext, useEffect, useState } from 'react'
4
+
5
+ type Theme = 'light' | 'dark'
6
+
7
+ type ThemeContextType = {
8
+ theme: Theme
9
+ toggleTheme: () => void
10
+ }
11
+
12
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
13
+
14
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
15
+ const [theme, setTheme] = useState<Theme>('light')
16
+
17
+ useEffect(() => {
18
+ // On mount, read theme from localStorage or default to light
19
+ const savedTheme = localStorage.getItem('theme') as Theme
20
+ if (savedTheme) {
21
+ setTheme(savedTheme)
22
+ document.documentElement.setAttribute('data-theme', savedTheme)
23
+ }
24
+ }, [])
25
+
26
+ const toggleTheme = () => {
27
+ const newTheme = theme === 'light' ? 'dark' : 'light'
28
+ setTheme(newTheme)
29
+ localStorage.setItem('theme', newTheme)
30
+ document.documentElement.setAttribute('data-theme', newTheme)
31
+ }
32
+
33
+ return (
34
+ <ThemeContext.Provider value={{ theme, toggleTheme }}>
35
+ {children}
36
+ </ThemeContext.Provider>
37
+ )
38
+ }
39
+
40
+ export function useTheme() {
41
+ const context = useContext(ThemeContext)
42
+ if (context === undefined) {
43
+ throw new Error('useTheme must be used within a ThemeProvider')
44
+ }
45
+ return context
46
+ }
app/favicon.ico ADDED
app/fonts/GeistMonoVF.woff ADDED
Binary file (67.9 kB). View file
 
app/fonts/GeistVF.woff ADDED
Binary file (66.3 kB). View file
 
app/globals.css ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ /* Light mode */
7
+ --background-primary: #ffffff;
8
+ --background-secondary: #f8f9fa;
9
+ --background-tertiary: #f1f3f5;
10
+
11
+ --text-primary: #171717;
12
+ --text-secondary: #4b5563;
13
+ --text-tertiary: #6b7280;
14
+
15
+ --border: #e5e7eb;
16
+ --border-secondary: #d1d5db;
17
+
18
+ --card-background: rgba(255, 255, 255, 0.8);
19
+ --card-border: rgba(229, 231, 235, 0.5);
20
+ --glass-background: rgba(255, 255, 255, 0.7);
21
+ }
22
+
23
+ [data-theme='dark'] {
24
+ /* Dark mode - updated for better contrast and aesthetics */
25
+ --background-primary: #0f172a; /* Darker blue-gray */
26
+ --background-secondary: #1e293b; /* Rich blue-gray */
27
+ --background-tertiary: #334155; /* Medium blue-gray */
28
+
29
+ --text-primary: #f8fafc; /* Very light blue-gray */
30
+ --text-secondary: #cbd5e1; /* Light blue-gray */
31
+ --text-tertiary: #94a3b8; /* Medium blue-gray */
32
+
33
+ --border: #334155; /* Medium blue-gray */
34
+ --border-secondary: #475569; /* Light blue-gray */
35
+
36
+ --card-background: rgba(30, 41, 59, 0.8); /* Transparent blue-gray */
37
+ --card-border: rgba(51, 65, 85, 0.5); /* Transparent border */
38
+ --glass-background: rgba(15, 23, 42, 0.7); /* Transparent dark blue-gray */
39
+ }
40
+
41
+ body {
42
+ color: var(--text-primary);
43
+ background: var(--background-primary);
44
+ }
45
+
46
+ /* Add smooth transitions for theme switching */
47
+ body * {
48
+ transition: background-color 0.3s ease, border-color 0.3s ease;
49
+ }
app/layout.tsx ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Metadata } from "next";
2
+ import localFont from "next/font/local";
3
+ import "./globals.css";
4
+ import { ThemeProvider } from './components/ThemeProvider'
5
+
6
+ const geistSans = localFont({
7
+ src: "./fonts/GeistVF.woff",
8
+ variable: "--font-geist-sans",
9
+ weight: "100 900",
10
+ });
11
+
12
+ export const metadata: Metadata = {
13
+ title: "PlayGo AI - AI-Powered Learning Platform",
14
+ description: "Transform education with AI-powered interactive learning experiences",
15
+ };
16
+
17
+ export default function RootLayout({
18
+ children,
19
+ }: {
20
+ children: React.ReactNode
21
+ }) {
22
+ return (
23
+ <html lang="en" className={`${geistSans.variable}`}>
24
+ <body className="antialiased">
25
+ <ThemeProvider>
26
+ {children}
27
+ </ThemeProvider>
28
+ </body>
29
+ </html>
30
+ )
31
+ }
app/page.tsx ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import Link from "next/link"
4
+ import { useTheme } from './components/ThemeProvider'
5
+ import { useEffect, useState } from 'react'
6
+ import ChatBot from './components/ChatBot'
7
+
8
+ export default function LandingPage() {
9
+ const { theme, toggleTheme } = useTheme()
10
+ const [message, setMessage] = useState('')
11
+
12
+ useEffect(() => {
13
+ // Call FastAPI endpoint
14
+ fetch('/api/hello')
15
+ .then(res => res.json())
16
+ .then(data => {setMessage(data.message); console.log('Fetched message:', data.message);})
17
+ .catch(err => console.error('Error fetching:', err))
18
+ }, [])
19
+
20
+ return (
21
+ <div className="min-h-screen bg-background-primary">
22
+ {/* Modern Header */}
23
+ <header className="fixed w-full bg-background-secondary/80 backdrop-blur-sm border-b border-border z-50">
24
+ <div className="container mx-auto px-4 py-4 flex items-center justify-between">
25
+ <Link href="/" className="text-3xl font-bold">
26
+ <span className="text-[#FF6B6B]">P</span>
27
+ <span className="text-[#4ECDC4]">l</span>
28
+ <span className="text-[#45B7D1]">a</span>
29
+ <span className="text-[#FDCB6E]">y</span>
30
+ <span className="text-[#FF6B6B]">G</span>
31
+ <span className="text-[#4ECDC4]">o</span>
32
+ <span className="ml-2 text-[#45B7D1]">A</span>
33
+ <span className="text-[#FDCB6E]">I</span>
34
+ </Link>
35
+ <nav className="flex items-center space-x-8">
36
+ <Link href="/for-teachers" className="text-text-secondary hover:text-primary">
37
+ For Teachers
38
+ </Link>
39
+ <Link href="/for-students" className="text-text-secondary hover:text-primary">
40
+ For Students
41
+ </Link>
42
+ <Link href="/community" className="text-text-secondary hover:text-primary">
43
+ Community
44
+ </Link>
45
+ <button
46
+ onClick={toggleTheme}
47
+ className="px-4 py-2 rounded-lg bg-primary text-white hover:bg-primary/90"
48
+ >
49
+ {theme === 'light' ? '🌙' : '☀️'}
50
+ </button>
51
+ </nav>
52
+ </div>
53
+ </header>
54
+
55
+ {/* Add ChatBot right after header */}
56
+ <div className="pt-20">
57
+ <ChatBot />
58
+ </div>
59
+
60
+ <main className="pt-4">
61
+ {/* Hero Section */}
62
+ <section className="container mx-auto px-4 py-20 text-center">
63
+ <h1 className="text-6xl font-bold mb-6 bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
64
+ Turn learning into an AI-powered adventure
65
+ </h1>
66
+ <p className="text-text-secondary text-xl mb-12 max-w-2xl mx-auto">
67
+ PlayGo AI is the leading educational platform for building engaging AI-powered learning experiences
68
+ </p>
69
+ <div className="flex justify-center gap-4">
70
+ <Link
71
+ href="/get-started"
72
+ className="px-6 py-3 rounded-lg bg-primary text-white hover:bg-primary/90 font-medium"
73
+ >
74
+ Get Started
75
+ </Link>
76
+ <Link
77
+ href="/demo"
78
+ className="px-6 py-3 rounded-lg border border-border hover:border-primary text-text-primary font-medium"
79
+ >
80
+ View Demo
81
+ </Link>
82
+ </div>
83
+ </section>
84
+
85
+ {/* Features Grid */}
86
+ <section className="bg-background-secondary py-20">
87
+ <div className="container mx-auto px-4">
88
+ <h2 className="text-3xl font-bold text-center mb-16">Build with our powerful learning tools</h2>
89
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
90
+ <div className="p-6 rounded-xl bg-background-primary">
91
+ <div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
92
+ <svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
93
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
94
+ </svg>
95
+ </div>
96
+ <h3 className="text-xl font-semibold mb-2">Interactive Learning</h3>
97
+ <p className="text-text-secondary">Engage with AI-powered quizzes, projects, and virtual labs</p>
98
+ </div>
99
+
100
+ <div className="p-6 rounded-xl bg-background-primary">
101
+ <div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
102
+ <svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
103
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
104
+ </svg>
105
+ </div>
106
+ <h3 className="text-xl font-semibold mb-2">Diverse Subjects</h3>
107
+ <p className="text-text-secondary">Explore topics from STEM to humanities with expert AI guidance</p>
108
+ </div>
109
+
110
+ <div className="p-6 rounded-xl bg-background-primary">
111
+ <div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
112
+ <svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
113
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
114
+ </svg>
115
+ </div>
116
+ <h3 className="text-xl font-semibold mb-2">Personalized Path</h3>
117
+ <p className="text-text-secondary">Adaptive learning tailored to your unique pace and style</p>
118
+ </div>
119
+
120
+ <div className="p-6 rounded-xl bg-background-primary">
121
+ <div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
122
+ <svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
123
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
124
+ </svg>
125
+ </div>
126
+ <h3 className="text-xl font-semibold mb-2">Progress Tracking</h3>
127
+ <p className="text-text-secondary">Monitor learning outcomes with detailed analytics</p>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </section>
132
+
133
+ {/* Stats Section */}
134
+ <section className="container mx-auto px-4 py-20">
135
+ <h2 className="text-3xl font-bold text-center mb-16">Trusted by educators worldwide</h2>
136
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
137
+ <div>
138
+ <p className="text-4xl font-bold text-primary mb-2">500K+</p>
139
+ <p className="text-text-secondary">Active Students</p>
140
+ </div>
141
+ <div>
142
+ <p className="text-4xl font-bold text-primary mb-2">10K+</p>
143
+ <p className="text-text-secondary">Teachers</p>
144
+ </div>
145
+ <div>
146
+ <p className="text-4xl font-bold text-primary mb-2">1M+</p>
147
+ <p className="text-text-secondary">Learning Sessions</p>
148
+ </div>
149
+ <div>
150
+ <p className="text-4xl font-bold text-primary mb-2">50+</p>
151
+ <p className="text-text-secondary">Countries</p>
152
+ </div>
153
+ </div>
154
+ </section>
155
+ </main>
156
+
157
+ {/* Updated Educational Footer */}
158
+ <footer className="bg-background-secondary border-t border-border py-12">
159
+ <div className="container mx-auto px-4">
160
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
161
+ <div className="text-center md:text-left">
162
+ <h3 className="text-2xl font-bold mb-4">
163
+ <span className="text-[#FF6B6B]">P</span>
164
+ <span className="text-[#4ECDC4]">l</span>
165
+ <span className="text-[#45B7D1]">a</span>
166
+ <span className="text-[#FDCB6E]">y</span>
167
+ <span className="text-[#FF6B6B]">G</span>
168
+ <span className="text-[#4ECDC4]">o</span>
169
+ <span className="ml-2 text-[#45B7D1]">A</span>
170
+ <span className="text-[#FDCB6E]">I</span>
171
+ </h3>
172
+ <p className="text-text-secondary">Making learning fun and accessible for everyone</p>
173
+ </div>
174
+
175
+ <div className="text-center">
176
+ <h3 className="font-semibold mb-4">Learning Resources</h3>
177
+ <ul className="space-y-2">
178
+ <li><Link href="/library" className="text-text-secondary hover:text-primary">Learning Library</Link></li>
179
+ <li><Link href="/tutorials" className="text-text-secondary hover:text-primary">Tutorials</Link></li>
180
+ <li><Link href="/blog" className="text-text-secondary hover:text-primary">Educational Blog</Link></li>
181
+ </ul>
182
+ </div>
183
+
184
+ <div className="text-center md:text-right">
185
+ <h3 className="font-semibold mb-4">Connect With Us</h3>
186
+ <div className="flex justify-center md:justify-end space-x-4">
187
+ <a href="#" className="text-text-secondary hover:text-primary" aria-label="Discord">
188
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
189
+ <path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"/>
190
+ </svg>
191
+ </a>
192
+ <a href="#" className="text-text-secondary hover:text-primary" aria-label="Twitter">
193
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
194
+ <path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
195
+ </svg>
196
+ </a>
197
+ <a href="#" className="text-text-secondary hover:text-primary" aria-label="YouTube">
198
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
199
+ <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
200
+ </svg>
201
+ </a>
202
+ </div>
203
+ <p className="mt-4 text-text-secondary">
204
+ Contact us: <a href="mailto:[email protected]" className="hover:text-primary">[email protected]</a>
205
+ </p>
206
+ </div>
207
+ </div>
208
+ <div className="mt-12 pt-8 border-t border-border/50 text-center text-text-secondary">
209
+ <p>PlayGo AI - A non-profit organization dedicated to making education accessible through AI</p>
210
+ <p className="mt-2">&copy; {new Date().getFullYear()} PlayGo AI. All rights reserved.</p>
211
+ </div>
212
+ </div>
213
+ </footer>
214
+ </div>
215
+ )
216
+ }
backend/main.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+
4
+ from fastapi import FastAPI, HTTPException, Query
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from fastapi.responses import StreamingResponse
7
+ from pydantic import BaseModel
8
+ import openai
9
+ from dotenv import load_dotenv
10
+ from typing import List
11
+ import asyncio
12
+
13
+ # Load environment variables
14
+ load_dotenv()
15
+
16
+ # Initialize OpenAI client
17
+ client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
18
+
19
+ app = FastAPI()
20
+
21
+ app.add_middleware(
22
+ CORSMiddleware,
23
+ allow_origins=["http://localhost:3000"],
24
+ allow_credentials=True,
25
+ allow_methods=["*"],
26
+ allow_headers=["*"],
27
+ )
28
+
29
+ class Message(BaseModel):
30
+ content: str
31
+ role: str
32
+
33
+ class ChatRequest(BaseModel):
34
+ messages: List[Message]
35
+
36
+ async def stream_text(messages: List[Message]):
37
+ try:
38
+ formatted_messages = [
39
+ {"role": "system", "content": """You are an AI learning assistant for PlayGo AI,
40
+ an educational platform. Your goal is to help students learn and understand various
41
+ subjects. Provide clear, concise, and accurate explanations."""},
42
+ ] + [{"role": msg.role, "content": msg.content} for msg in messages]
43
+
44
+ stream = client.chat.completions.create(
45
+ model="gpt-3.5-turbo",
46
+ messages=formatted_messages,
47
+ temperature=0.7,
48
+ stream=True
49
+ )
50
+
51
+ for chunk in stream:
52
+ for choice in chunk.choices:
53
+ if choice.finish_reason == "stop":
54
+ continue
55
+
56
+ else:
57
+ yield '0:{text}\n'.format(text=json.dumps(choice.delta.content))
58
+
59
+ if chunk.choices == []:
60
+ usage = chunk.usage
61
+ prompt_tokens = usage.prompt_tokens
62
+ completion_tokens = usage.completion_tokens
63
+ yield 'd:{{"finishReason":"{reason}","usage":{{"promptTokens":{prompt},"completionTokens":{completion}}}}}\n'.format(
64
+ reason="stop",
65
+ prompt=prompt_tokens,
66
+ completion=completion_tokens
67
+ )
68
+
69
+ except Exception as e:
70
+ print(f"Error in stream_text: {str(e)}")
71
+ yield f"Error: {str(e)}".encode('utf-8')
72
+
73
+ @app.post("/api/landing_page_chat")
74
+ async def landing_page_chat(request: ChatRequest,):
75
+ response = StreamingResponse(
76
+ stream_text(request.messages),
77
+ )
78
+ response.headers['x-vercel-ai-data-stream'] = 'v1'
79
+ return response
80
+
81
+ @app.get("/api/hello")
82
+ async def root():
83
+ return {"message": "Hello, World!"}
backend/requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi[standard]
2
+ uvicorn>=0.15.0
3
+ pydantic>=1.8.0
4
+ python-dotenv>=0.19.0
5
+ openai>=1.0.0
6
+ python-dotenv>=0.19.0
next.config.ts ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ output: 'standalone',
5
+ async rewrites() {
6
+ return [
7
+ {
8
+ source: '/api/:path*',
9
+ destination: 'http://localhost:8000/api/:path*' // FastAPI backend
10
+ }
11
+ ]
12
+ }
13
+ };
14
+
15
+ export default nextConfig;
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "playgo_next",
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
+ "ai": "^3.4.31",
13
+ "next": "15.0.2",
14
+ "react": "19.0.0-rc-02c0e824-20241028",
15
+ "react-dom": "19.0.0-rc-02c0e824-20241028"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^20",
19
+ "@types/react": "^18",
20
+ "@types/react-dom": "^18",
21
+ "@types/tailwindcss": "^3.0.11",
22
+ "eslint": "^8",
23
+ "eslint-config-next": "15.0.2",
24
+ "postcss": "^8",
25
+ "tailwindcss": "^3.4.14",
26
+ "typescript": "^5"
27
+ }
28
+ }
pnpm-lock.yaml ADDED
The diff for this file is too large to render. See raw diff
 
postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
public/file.svg ADDED
public/globe.svg ADDED
public/next.svg ADDED
public/vercel.svg ADDED
public/window.svg ADDED
tailwind.config.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Config } from "tailwindcss";
2
+
3
+ const config: Config = {
4
+ darkMode: 'class',
5
+ content: [
6
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
7
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
8
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
9
+ ],
10
+ theme: {
11
+ extend: {
12
+ colors: {
13
+ // Brand colors - slightly adjusted for dark mode compatibility
14
+ primary: {
15
+ DEFAULT: '#4ECDC4',
16
+ dark: '#3DAA9D',
17
+ light: '#7EDCD6',
18
+ },
19
+ secondary: {
20
+ DEFAULT: '#FF6B6B',
21
+ dark: '#E65555',
22
+ light: '#FF8F8F',
23
+ },
24
+ accent: {
25
+ blue: '#45B7D1',
26
+ yellow: '#FDCB6E',
27
+ dark: {
28
+ blue: '#3A9BB2',
29
+ yellow: '#D4A85D',
30
+ },
31
+ },
32
+ // Semantic colors
33
+ background: {
34
+ primary: 'var(--background-primary)',
35
+ secondary: 'var(--background-secondary)',
36
+ tertiary: 'var(--background-tertiary)',
37
+ card: 'var(--card-background)',
38
+ glass: 'var(--glass-background)',
39
+ },
40
+ text: {
41
+ primary: 'var(--text-primary)',
42
+ secondary: 'var(--text-secondary)',
43
+ tertiary: 'var(--text-tertiary)',
44
+ },
45
+ border: {
46
+ DEFAULT: 'var(--border)',
47
+ secondary: 'var(--border-secondary)',
48
+ card: 'var(--card-border)',
49
+ },
50
+ },
51
+ backdropBlur: {
52
+ xs: '2px',
53
+ },
54
+ boxShadow: {
55
+ 'dark': '0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.24)',
56
+ },
57
+ },
58
+ },
59
+ plugins: [],
60
+ };
61
+ export default config;
tsconfig.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
+ "exclude": ["node_modules"]
27
+ }