├── src └── app │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── api │ └── analyze │ │ └── route.ts │ ├── session-analyzer │ └── page.tsx │ └── page.tsx ├── postcss.config.mjs ├── public ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── next.config.ts ├── eslint.config.mjs ├── .gitignore ├── package.json ├── tsconfig.json └── README.md /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rohanmistry231/ai-mind-playground/master/src/app/favicon.ico -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | 7 | export default config; 8 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | reactCompiler: true, 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig, globalIgnores } from "eslint/config"; 2 | import nextVitals from "eslint-config-next/core-web-vitals"; 3 | import nextTs from "eslint-config-next/typescript"; 4 | 5 | const eslintConfig = defineConfig([ 6 | ...nextVitals, 7 | ...nextTs, 8 | // Override default ignores of eslint-config-next. 9 | globalIgnores([ 10 | // Default ignores of eslint-config-next: 11 | ".next/**", 12 | "out/**", 13 | "build/**", 14 | "next-env.d.ts", 15 | ]), 16 | ]); 17 | 18 | export default eslintConfig; 19 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /.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.* 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 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai-mind-playground", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint" 10 | }, 11 | "dependencies": { 12 | "@google/genai": "^1.31.0", 13 | "next": "16.0.7", 14 | "react": "19.2.0", 15 | "react-dom": "19.2.0", 16 | "recharts": "^3.5.1" 17 | }, 18 | "devDependencies": { 19 | "@tailwindcss/postcss": "^4", 20 | "@types/node": "^20", 21 | "@types/react": "^19", 22 | "@types/react-dom": "^19", 23 | "babel-plugin-react-compiler": "1.0.0", 24 | "eslint": "^9", 25 | "eslint-config-next": "16.0.7", 26 | "tailwindcss": "^4", 27 | "typescript": "^5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 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": "react-jsx", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": [ 26 | "next-env.d.ts", 27 | "**/*.ts", 28 | "**/*.tsx", 29 | ".next/types/**/*.ts", 30 | ".next/dev/types/**/*.ts", 31 | "**/*.mts" 32 | ], 33 | "exclude": ["node_modules"] 34 | } 35 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/api/analyze/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { GoogleGenAI } from "@google/genai"; 3 | 4 | const apiKey = process.env.GEMINI_API_KEY; 5 | 6 | if (!apiKey) { 7 | console.warn("⚠️ GEMINI_API_KEY is not set in .env.local"); 8 | } 9 | 10 | const genAI = apiKey ? new GoogleGenAI({ apiKey }) : null; 11 | 12 | export async function POST(req: Request) { 13 | try { 14 | if (!genAI) { 15 | return NextResponse.json( 16 | { error: "Gemini API key not configured on server." }, 17 | { status: 500 } 18 | ); 19 | } 20 | 21 | const body = await req.json(); 22 | const { prompt } = body as { prompt?: string }; 23 | 24 | if (!prompt || !prompt.trim()) { 25 | return NextResponse.json( 26 | { error: "Prompt is required." }, 27 | { status: 400 } 28 | ); 29 | } 30 | 31 | const instruction = ` 32 | You are an assistant that analyzes how people use AI models. 33 | Given a single user prompt (something they would ask ChatGPT/Gemini), 34 | you will classify it and score it based on how much it grows their mind 35 | versus how much it makes them dependent. 36 | 37 | Respond ONLY with valid JSON in this exact format, no markdown, no extra text: 38 | 39 | { 40 | "promptType": "Learning" | "Delegation" | "Creation" | "Trivial" | "Mixed", 41 | "growthScore": 0-100, 42 | "dependencyScore": 0-100, 43 | "shortFeedback": "one or two sentences of constructive feedback" 44 | } 45 | 46 | User prompt: 47 | """${prompt}""" 48 | `.trim(); 49 | 50 | const response = await genAI.models.generateContent({ 51 | model: "gemini-2.5-flash", 52 | contents: instruction, 53 | }); 54 | 55 | // In the new SDK, text is a property, NOT a function 56 | const rawText = (response.text ?? "").toString().trim(); 57 | 58 | console.log("📦 RAW GEMINI RESPONSE:", rawText); 59 | 60 | let parsed: any; 61 | try { 62 | parsed = JSON.parse(rawText); 63 | } catch (err) { 64 | console.error("❌ JSON parse failed:", rawText); 65 | return NextResponse.json( 66 | { error: "Failed to parse JSON from Gemini.", raw: rawText }, 67 | { status: 500 } 68 | ); 69 | } 70 | 71 | const safe = { 72 | promptType: parsed.promptType ?? "Unknown", 73 | growthScore: Math.max( 74 | 0, 75 | Math.min(100, Number(parsed.growthScore) || 0) 76 | ), 77 | dependencyScore: Math.max( 78 | 0, 79 | Math.min(100, Number(parsed.dependencyScore) || 0) 80 | ), 81 | shortFeedback: parsed.shortFeedback ?? "", 82 | }; 83 | 84 | console.log("✅ Parsed analysis:", safe); 85 | 86 | return NextResponse.json(safe); 87 | } catch (error) { 88 | console.error("💥 Error in /api/analyze:", error); 89 | return NextResponse.json( 90 | { error: "Unexpected server error." }, 91 | { status: 500 } 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧠 AI Mind Playground 2 | 3 | ### **Analyze how you use AI — Are you growing your brain, or outsourcing your thinking?** 4 | 5 | AI Mind Playground is a smart analytics tool that evaluates your prompts and interactions with Large Language Models (ChatGPT, Gemini, Claude, etc.) to determine whether they **expand your understanding** or **increase dependency**. 6 | 7 | This project offers two powerful modes: 8 | 9 | - **Playground Mode** — Analyze single prompts in real-time and receive AI-powered insight. 10 | - **Session Analyzer** — Paste your entire chat history and get a full report on learning vs dependency. 11 | 12 | --- 13 | 14 | ## 🚀 Live Demo 15 | 🔗 https://ai-mind-playground.vercel.app/ 16 | 17 | --- 18 | 19 | ## 🎯 Why This Project Exists 20 | 21 | AI tools are changing how humans think, write, learn, and solve problems. 22 | 23 | But here's the real question: 24 | 25 | > **Are we becoming smarter with AI, or more dependent on it?** 26 | 27 | This project tracks your AI usage pattern and returns metrics such as: 28 | - **Brain Growth Score** 29 | - **Dependency Score** 30 | - **Detected Prompt Type** 31 | - **Insight Suggestion** 32 | - **Session Breakdown & Averages** 33 | 34 | --- 35 | 36 | ## 🧩 Core Features 37 | 38 | | Feature | Description | 39 | |--------|------------| 40 | | 🔎 Prompt Intelligence | Classifies your prompt (Learning, Delegation, Creation, Trivial). | 41 | | 📊 Brain Growth Analytics | Shows how much your prompt leads to actual skill development. | 42 | | 🔁 Dependency Score | Detects if you're outsourcing thinking & task completion. | 43 | | 🧠 Gemini AI Integration | Uses `gemini-2.5-flash` for intelligent reporting. | 44 | | 🧮 Local Fallback AI | Works even if API fails using rule-based heuristic scoring. | 45 | | 📈 Session Summary | Paste chat history & evaluate overall learning vs dependency. | 46 | | 📋 Prompt History Log | Tracks past prompts during your session. | 47 | | 📉 Charts | Recharts-based visual session insights. | 48 | 49 | --- 50 | 51 | ## 🛠 Tech Stack 52 | 53 | | Layer | Technology | 54 | |------|-----------| 55 | | Frontend | **Next.js 14**, React, Tailwind CSS | 56 | | Backend API | Next.js Route Handlers | 57 | | AI Model | **Google Gemini (`gemini-2.5-flash`)** | 58 | | Charts | Recharts | 59 | | Deployment | **Vercel** | 60 | | Environment | `.env.local` | 61 | 62 | --- 63 | 64 | ## ⚙ Environment Setup 65 | 66 | Create `.env.local`: 67 | 68 | ``` 69 | 70 | GEMINI_API_KEY=your_google_gemini_api_key_here 71 | 72 | ```` 73 | 74 | Install packages: 75 | 76 | ```bash 77 | npm install 78 | ```` 79 | 80 | Run locally: 81 | 82 | ```bash 83 | npm run dev 84 | ``` 85 | 86 | Build: 87 | 88 | ```bash 89 | npm run build 90 | npm start 91 | ``` 92 | 93 | --- 94 | 95 | ## 📂 Folder Structure 96 | 97 | ``` 98 | ai-mind-playground/ 99 | │── app/ 100 | │ ├── page.tsx # Playground analyzer UI 101 | │ ├── session-analyzer/ # Session level analyzer 102 | │ │ └── page.tsx 103 | │ └── api/ 104 | │ └── analyze/ 105 | │ └── route.ts # Gemini AI prompt scoring 106 | │ 107 | │── public/ 108 | │ 109 | │── package.json 110 | │── README.md 111 | │── .env.local (ignored) 112 | ``` 113 | 114 | --- 115 | 116 | ## 🚀 Deployment Guide (Quick) 117 | 118 | This project is optimized for **Vercel**. 119 | 120 | 1. Push to GitHub 121 | 2. Import to Vercel 122 | 3. Add `GEMINI_API_KEY` in Vercel > Project Settings > Environment Variables 123 | 4. Deploy 🎉 124 | 125 | --- 126 | 127 | ## 🌟 Use Cases 128 | 129 | ✔ AI learning pattern detection 130 | ✔ Productivity vs dependency analysis 131 | ✔ Research on AI-human cognition 132 | ✔ Educational tools 133 | ✔ AI safety / ethical studies 134 | ✔ Student learning dashboards 135 | 136 | --- 137 | 138 | ## 📢 Future Enhancements (Open to contributors) 139 | 140 | * 🧮 Exportable PDF Report 141 | * 📅 Multi-session tracking history (local + cloud) 142 | * 🤖 AI coach suggesting better prompts 143 | * 🔥 Leaderboard mode (Gamified brain growth) 144 | * 🔐 Auth + personal dashboard 145 | 146 | --- 147 | 148 | ## 🤝 Contributing 149 | 150 | Pull requests are welcome! 151 | If you find bugs, open an issue with details. 152 | 153 | --- 154 | 155 | ## ⭐ Support the Project 156 | 157 | If this project inspired you, drop a ⭐ on GitHub! 158 | Your star helps this project grow and reach more developers. 159 | 160 | ``` 161 | ⭐ Go to top → Click “Star” 162 | ``` 163 | 164 | --- 165 | 166 | ## Made with ❤️ and 🔥 167 | 168 | By **AI + Human Mind Collaboration** -------------------------------------------------------------------------------- /src/app/session-analyzer/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | import Link from "next/link"; 5 | 6 | type SessionPrompt = { 7 | id: number; 8 | text: string; 9 | type: string; 10 | growth: number; 11 | dependency: number; 12 | }; 13 | 14 | const SESSION_DAILY_LIMIT = 5; 15 | const SESSION_USAGE_KEY = "ai-mind-playground-session-usage"; 16 | 17 | export default function SessionAnalyzerPage() { 18 | const [rawText, setRawText] = useState(""); 19 | const [isAnalyzing, setIsAnalyzing] = useState(false); 20 | const [prompts, setPrompts] = useState([]); 21 | const [overallGrowth, setOverallGrowth] = useState(null); 22 | const [overallDependency, setOverallDependency] = useState( 23 | null 24 | ); 25 | const [error, setError] = useState(null); 26 | const [remainingToday, setRemainingToday] = useState(null); 27 | 28 | // ----- daily limit helpers ----- 29 | 30 | const initDailyUsage = () => { 31 | if (typeof window === "undefined") return; 32 | 33 | const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD 34 | const raw = localStorage.getItem(SESSION_USAGE_KEY); 35 | 36 | if (!raw) { 37 | localStorage.setItem( 38 | SESSION_USAGE_KEY, 39 | JSON.stringify({ date: today, count: 0 }) 40 | ); 41 | setRemainingToday(SESSION_DAILY_LIMIT); 42 | return; 43 | } 44 | 45 | try { 46 | const parsed = JSON.parse(raw) as { date: string; count: number }; 47 | 48 | if (parsed.date === today) { 49 | const remaining = Math.max( 50 | SESSION_DAILY_LIMIT - (parsed.count || 0), 51 | 0 52 | ); 53 | setRemainingToday(remaining); 54 | } else { 55 | // New day -> reset 56 | localStorage.setItem( 57 | SESSION_USAGE_KEY, 58 | JSON.stringify({ date: today, count: 0 }) 59 | ); 60 | setRemainingToday(SESSION_DAILY_LIMIT); 61 | } 62 | } catch { 63 | // corrupted -> reset 64 | localStorage.setItem( 65 | SESSION_USAGE_KEY, 66 | JSON.stringify({ date: today, count: 0 }) 67 | ); 68 | setRemainingToday(SESSION_DAILY_LIMIT); 69 | } 70 | }; 71 | 72 | const incrementUsage = () => { 73 | if (typeof window === "undefined") return; 74 | 75 | const today = new Date().toISOString().slice(0, 10); 76 | let date = today; 77 | let count = 0; 78 | 79 | const raw = localStorage.getItem(SESSION_USAGE_KEY); 80 | if (raw) { 81 | try { 82 | const parsed = JSON.parse(raw) as { date: string; count: number }; 83 | date = parsed.date; 84 | count = parsed.count || 0; 85 | } catch { 86 | // ignore, will reset below 87 | } 88 | } 89 | 90 | if (date !== today) { 91 | date = today; 92 | count = 0; 93 | } 94 | 95 | count += 1; 96 | 97 | localStorage.setItem( 98 | SESSION_USAGE_KEY, 99 | JSON.stringify({ date, count }) 100 | ); 101 | 102 | const remaining = Math.max(SESSION_DAILY_LIMIT - count, 0); 103 | setRemainingToday(remaining); 104 | }; 105 | 106 | useEffect(() => { 107 | initDailyUsage(); 108 | }, []); 109 | 110 | const handleAnalyzeSession = () => { 111 | const text = rawText.trim(); 112 | 113 | if (!text) { 114 | alert("Paste some conversation or prompts first."); 115 | return; 116 | } 117 | 118 | if (remainingToday !== null && remainingToday <= 0) { 119 | alert( 120 | `Daily limit reached. You can analyze ${SESSION_DAILY_LIMIT} sessions per day. Please come back tomorrow.` 121 | ); 122 | return; 123 | } 124 | 125 | setIsAnalyzing(true); 126 | setError(null); 127 | 128 | try { 129 | const lines = text 130 | .split(/\r?\n/) 131 | .map((line) => line.trim()) 132 | .filter((line) => line.length > 0); 133 | 134 | if (lines.length === 0) { 135 | setError("No valid lines found. Paste at least one prompt."); 136 | setPrompts([]); 137 | setOverallGrowth(null); 138 | setOverallDependency(null); 139 | return; 140 | } 141 | 142 | const analyzed: SessionPrompt[] = []; 143 | 144 | for (const line of lines) { 145 | const lower = line.toLowerCase(); 146 | 147 | let learning = 0; 148 | let delegation = 0; 149 | let creation = 0; 150 | let trivial = 0; 151 | 152 | if ( 153 | lower.includes("explain") || 154 | lower.includes("why") || 155 | lower.includes("how does") || 156 | lower.includes("teach me") || 157 | lower.includes("help me understand") 158 | ) { 159 | learning += 1; 160 | } 161 | 162 | if ( 163 | lower.includes("write full") || 164 | lower.includes("write an essay") || 165 | lower.includes("do this for me") || 166 | lower.includes("generate complete") || 167 | lower.includes("solve this for me") 168 | ) { 169 | delegation += 1; 170 | } 171 | 172 | if ( 173 | lower.includes("improve my") || 174 | lower.includes("review my") || 175 | lower.includes("refactor") || 176 | lower.includes("critique") || 177 | lower.includes("polish") 178 | ) { 179 | creation += 1; 180 | } 181 | 182 | if (lower.split(" ").length <= 4) { 183 | trivial += 1; 184 | } 185 | 186 | let type = "Mixed"; 187 | const maxVal = Math.max(learning, delegation, creation, trivial); 188 | 189 | if (maxVal === learning && learning > 0) type = "Learning"; 190 | else if (maxVal === delegation && delegation > 0) type = "Delegation"; 191 | else if (maxVal === creation && creation > 0) type = "Creation"; 192 | else if (maxVal === trivial && trivial > 0) type = "Trivial"; 193 | 194 | let growth = 50; 195 | let dependency = 50; 196 | 197 | if (type === "Learning") { 198 | growth = 85; 199 | dependency = 20; 200 | } else if (type === "Creation") { 201 | growth = 75; 202 | dependency = 35; 203 | } else if (type === "Delegation") { 204 | growth = 35; 205 | dependency = 80; 206 | } else if (type === "Trivial") { 207 | growth = 20; 208 | dependency = 60; 209 | } 210 | 211 | const wordCount = lower.split(/\s+/).length; 212 | if (wordCount > 20) { 213 | growth += 5; 214 | dependency -= 5; 215 | } else if (wordCount < 6) { 216 | growth -= 5; 217 | dependency += 5; 218 | } 219 | 220 | growth = Math.max(0, Math.min(100, growth)); 221 | dependency = Math.max(0, Math.min(100, dependency)); 222 | 223 | analyzed.push({ 224 | id: Date.now() + Math.random(), 225 | text: line, 226 | type, 227 | growth, 228 | dependency, 229 | }); 230 | } 231 | 232 | setPrompts(analyzed); 233 | 234 | const totalGrowth = analyzed.reduce((sum, p) => sum + p.growth, 0); 235 | const totalDependency = analyzed.reduce( 236 | (sum, p) => sum + p.dependency, 237 | 0 238 | ); 239 | 240 | setOverallGrowth(Math.round(totalGrowth / analyzed.length)); 241 | setOverallDependency(Math.round(totalDependency / analyzed.length)); 242 | 243 | // count one session analysis 244 | incrementUsage(); 245 | } catch (err) { 246 | console.error(err); 247 | setError("Something went wrong while analyzing the session."); 248 | setPrompts([]); 249 | setOverallGrowth(null); 250 | setOverallDependency(null); 251 | } finally { 252 | setIsAnalyzing(false); 253 | } 254 | }; 255 | 256 | const totalPrompts = prompts.length; 257 | const typeCounts = prompts.reduce( 258 | (acc, p) => { 259 | acc[p.type] = (acc[p.type] || 0) + 1; 260 | return acc; 261 | }, 262 | {} as Record 263 | ); 264 | 265 | const isLimitReached = 266 | remainingToday !== null && remainingToday <= 0; 267 | 268 | return ( 269 |
270 |
271 | {/* Top bar with nav */} 272 |
273 |
274 |

275 | Session Analyzer 276 |

277 |

278 | Paste a set of prompts or conversation from any LLM and get a 279 | session-level growth vs dependency report. 280 |

281 |
282 | 301 |
302 | 303 | {/* Input area */} 304 |
305 | 308 |