├── 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 |
329 |
330 | {/* Summary */}
331 |
332 | Session Summary
333 | {overallGrowth === null || overallDependency === null ? (
334 |
335 | No session analyzed yet. Paste prompts and click{" "}
336 |
337 | Analyze Session
338 |
339 | .
340 |
341 | ) : (
342 |
343 |
344 |
345 | Total Prompts
346 |
347 |
{totalPrompts}
348 |
349 |
350 |
351 | Avg Brain Growth Score
352 |
353 |
354 | {overallGrowth}/100
355 |
356 |
357 |
358 |
359 | Avg Dependency Score
360 |
361 |
362 | {overallDependency}/100
363 |
364 |
365 |
366 | )}
367 |
368 |
369 | {/* Type breakdown */}
370 | {prompts.length > 0 && (
371 |
372 | Prompt Type Breakdown
373 |
374 | {Object.entries(typeCounts).map(([type, count]) => {
375 | const percent = Math.round((count / totalPrompts) * 100);
376 | return (
377 |
381 |
{type}
382 |
383 | {count} prompts • {percent}%
384 |
385 |
386 | );
387 | })}
388 |
389 |
390 | )}
391 |
392 | {/* Per-prompt list */}
393 | {prompts.length > 0 && (
394 |
395 | Analyzed Prompts
396 |
397 | {prompts.map((item) => (
398 |
402 |
{item.text}
403 |
404 |
405 | {item.type}
406 |
407 |
408 | Growth:{" "}
409 |
410 | {item.growth}
411 |
412 |
413 |
414 | Dependency:{" "}
415 |
416 | {item.dependency}
417 |
418 |
419 |
420 |
421 | ))}
422 |
423 |
424 | )}
425 |
426 |
427 | );
428 | }
429 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect, useState } from "react";
4 | import Link from "next/link";
5 | import {
6 | ResponsiveContainer,
7 | BarChart,
8 | Bar,
9 | XAxis,
10 | YAxis,
11 | Tooltip,
12 | } from "recharts";
13 |
14 | type PromptRecord = {
15 | id: number;
16 | text: string;
17 | type: string;
18 | growth: number;
19 | dependency: number;
20 | source: "gemini" | "local";
21 | createdAt: string;
22 | };
23 |
24 | const DAILY_LIMIT = 5;
25 | const USAGE_KEY = "ai-mind-playground-usage";
26 |
27 | export default function Home() {
28 | const [prompt, setPrompt] = useState("");
29 | const [isLoading, setIsLoading] = useState(false);
30 | const [feedback, setFeedback] = useState(null);
31 | const [growthScore, setGrowthScore] = useState(null);
32 | const [dependencyScore, setDependencyScore] = useState(null);
33 | const [promptType, setPromptType] = useState(null);
34 | const [history, setHistory] = useState([]);
35 | const [remainingToday, setRemainingToday] = useState(null);
36 |
37 | // ----- daily limit helpers -----
38 |
39 | const initDailyUsage = () => {
40 | if (typeof window === "undefined") return;
41 |
42 | const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
43 | const raw = localStorage.getItem(USAGE_KEY);
44 |
45 | if (!raw) {
46 | localStorage.setItem(
47 | USAGE_KEY,
48 | JSON.stringify({ date: today, count: 0 })
49 | );
50 | setRemainingToday(DAILY_LIMIT);
51 | return;
52 | }
53 |
54 | try {
55 | const parsed = JSON.parse(raw) as { date: string; count: number };
56 |
57 | if (parsed.date === today) {
58 | const remaining = Math.max(DAILY_LIMIT - (parsed.count || 0), 0);
59 | setRemainingToday(remaining);
60 | } else {
61 | // New day -> reset
62 | localStorage.setItem(
63 | USAGE_KEY,
64 | JSON.stringify({ date: today, count: 0 })
65 | );
66 | setRemainingToday(DAILY_LIMIT);
67 | }
68 | } catch {
69 | // corrupted -> reset
70 | localStorage.setItem(
71 | USAGE_KEY,
72 | JSON.stringify({ date: today, count: 0 })
73 | );
74 | setRemainingToday(DAILY_LIMIT);
75 | }
76 | };
77 |
78 | const incrementUsage = () => {
79 | if (typeof window === "undefined") return;
80 |
81 | const today = new Date().toISOString().slice(0, 10);
82 | let date = today;
83 | let count = 0;
84 |
85 | const raw = localStorage.getItem(USAGE_KEY);
86 | if (raw) {
87 | try {
88 | const parsed = JSON.parse(raw) as { date: string; count: number };
89 | date = parsed.date;
90 | count = parsed.count || 0;
91 | } catch {
92 | // ignore and reset below
93 | }
94 | }
95 |
96 | if (date !== today) {
97 | date = today;
98 | count = 0;
99 | }
100 |
101 | count += 1;
102 |
103 | localStorage.setItem(
104 | USAGE_KEY,
105 | JSON.stringify({ date, count })
106 | );
107 |
108 | const remaining = Math.max(DAILY_LIMIT - count, 0);
109 | setRemainingToday(remaining);
110 | };
111 |
112 | useEffect(() => {
113 | initDailyUsage();
114 | }, []);
115 |
116 | const handleAnalyze = async () => {
117 | const text = prompt.toLowerCase().trim();
118 |
119 | if (!text) {
120 | alert("Please enter a prompt first.");
121 | return;
122 | }
123 |
124 | if (remainingToday !== null && remainingToday <= 0) {
125 | alert(
126 | `Daily limit reached. You can analyze ${DAILY_LIMIT} prompts per day. Please come back tomorrow.`
127 | );
128 | return;
129 | }
130 |
131 | setIsLoading(true);
132 | setFeedback(null);
133 |
134 | try {
135 | const res = await fetch("/api/analyze", {
136 | method: "POST",
137 | headers: {
138 | "Content-Type": "application/json",
139 | },
140 | body: JSON.stringify({ prompt }),
141 | });
142 |
143 | if (!res.ok) {
144 | console.warn("Gemini API error, falling back to local logic.");
145 | throw new Error("Gemini API error");
146 | }
147 |
148 | const data = await res.json();
149 |
150 | const type = data.promptType ?? "Unknown";
151 | const growth = data.growthScore ?? null;
152 | const dependency = data.dependencyScore ?? null;
153 | const shortFeedback = data.shortFeedback ?? null;
154 |
155 | setPromptType(type);
156 | setGrowthScore(growth);
157 | setDependencyScore(dependency);
158 | setFeedback(shortFeedback);
159 |
160 | if (growth !== null && dependency !== null) {
161 | setHistory((prev) => [
162 | {
163 | id: Date.now(),
164 | text: prompt,
165 | type,
166 | growth,
167 | dependency,
168 | source: "gemini",
169 | createdAt: new Date().toLocaleTimeString(),
170 | },
171 | ...prev,
172 | ]);
173 | }
174 |
175 | // only increment usage when we actually ran an analysis
176 | incrementUsage();
177 | } catch (err) {
178 | console.error("Error analyzing with Gemini, using fallback:", err);
179 |
180 | let learning = 0;
181 | let delegation = 0;
182 | let creation = 0;
183 | let trivial = 0;
184 |
185 | if (
186 | text.includes("explain") ||
187 | text.includes("why") ||
188 | text.includes("how does") ||
189 | text.includes("teach me") ||
190 | text.includes("help me understand")
191 | ) {
192 | learning += 1;
193 | }
194 |
195 | if (
196 | text.includes("write full") ||
197 | text.includes("write an essay") ||
198 | text.includes("do this for me") ||
199 | text.includes("generate complete") ||
200 | text.includes("solve this for me")
201 | ) {
202 | delegation += 1;
203 | }
204 |
205 | if (
206 | text.includes("improve my") ||
207 | text.includes("review my") ||
208 | text.includes("refactor") ||
209 | text.includes("critique") ||
210 | text.includes("polish")
211 | ) {
212 | creation += 1;
213 | }
214 |
215 | if (text.split(" ").length <= 4) {
216 | trivial += 1;
217 | }
218 |
219 | let type = "Mixed / Neutral";
220 | const maxVal = Math.max(learning, delegation, creation, trivial);
221 |
222 | if (maxVal === learning && learning > 0) type = "Learning";
223 | else if (maxVal === delegation && delegation > 0) type = "Delegation";
224 | else if (maxVal === creation && creation > 0) type = "Creation";
225 | else if (maxVal === trivial && trivial > 0) type = "Trivial";
226 |
227 | let growth = 50;
228 | let dependency = 50;
229 |
230 | if (type === "Learning") {
231 | growth = 85;
232 | dependency = 20;
233 | } else if (type === "Creation") {
234 | growth = 75;
235 | dependency = 35;
236 | } else if (type === "Delegation") {
237 | growth = 35;
238 | dependency = 80;
239 | } else if (type === "Trivial") {
240 | growth = 20;
241 | dependency = 60;
242 | }
243 |
244 | const wordCount = text.split(/\s+/).length;
245 | if (wordCount > 20) {
246 | growth += 5;
247 | dependency -= 5;
248 | } else if (wordCount < 6) {
249 | growth -= 5;
250 | dependency += 5;
251 | }
252 |
253 | growth = Math.max(0, Math.min(100, growth));
254 | dependency = Math.max(0, Math.min(100, dependency));
255 |
256 | setPromptType(type);
257 | setGrowthScore(growth);
258 | setDependencyScore(dependency);
259 | setFeedback(
260 | "Using offline heuristic analysis because the AI analysis is not available right now."
261 | );
262 |
263 | setHistory((prev) => [
264 | {
265 | id: Date.now(),
266 | text: prompt,
267 | type,
268 | growth,
269 | dependency,
270 | source: "local",
271 | createdAt: new Date().toLocaleTimeString(),
272 | },
273 | ...prev,
274 | ]);
275 |
276 | // fallback also counts as one "analysis" for quota
277 | incrementUsage();
278 | } finally {
279 | setIsLoading(false);
280 | }
281 | };
282 |
283 | const hasHistory = history.length > 0;
284 |
285 | const avgGrowth =
286 | history.length > 0
287 | ? Math.round(
288 | history.reduce((sum, item) => sum + item.growth, 0) / history.length
289 | )
290 | : 0;
291 |
292 | const avgDependency =
293 | history.length > 0
294 | ? Math.round(
295 | history.reduce((sum, item) => sum + item.dependency, 0) /
296 | history.length
297 | )
298 | : 0;
299 |
300 | const chartData = [
301 | { name: "Avg Growth", value: avgGrowth },
302 | { name: "Avg Dependency", value: avgDependency },
303 | ];
304 |
305 | const isLimitReached =
306 | remainingToday !== null && remainingToday <= 0;
307 |
308 | return (
309 |
310 |
311 | {/* Top bar with nav */}
312 |
313 |
314 |
315 | AI Mind Playground
316 |
317 |
318 | Type a prompt you would normally ask ChatGPT/Gemini and see
319 | whether it grows your mind or makes you more dependent.
320 |
321 |
322 |
341 |
342 |
343 | {/* Prompt input */}
344 |
345 |
348 |
366 |
367 | {/* Session Overview / Chart */}
368 |
369 | Session Overview
370 | {!hasHistory ? (
371 |
372 | Analyze a few prompts to see your average Brain Growth and
373 | Dependency scores for this session.
374 |
375 | ) : (
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 | )}
387 |
388 |
389 | {/* Single prompt report */}
390 |
391 | Prompt Intelligence Report
392 | {growthScore === null && dependencyScore === null ? (
393 |
394 | No analysis yet. Enter a prompt and click{" "}
395 | Analyze Prompt
396 | .
397 |
398 | ) : (
399 | <>
400 |
401 |
402 |
403 | Brain Growth Score
404 |
405 |
{growthScore}/100
406 |
407 |
408 |
409 | Dependency Score
410 |
411 |
412 | {dependencyScore}/100
413 |
414 |
415 |
416 |
417 | Detected Prompt Type
418 |
419 |
{promptType ?? "—"}
420 |
421 |
422 |
423 | {feedback && (
424 |
425 | Insight:{" "}
426 | {feedback}
427 |
428 | )}
429 | >
430 | )}
431 |
432 |
433 | {/* Prompt history */}
434 |
435 | Prompt History
436 | {history.length === 0 ? (
437 |
438 | No prompts analyzed yet. Your recent prompts will appear here with
439 | their growth and dependency scores.
440 |
441 | ) : (
442 |
443 | {history.map((item) => (
444 |
448 |
449 |
450 | {item.text}
451 |
452 |
453 |
454 | {item.type}
455 |
456 |
457 | Growth:{" "}
458 |
459 | {item.growth}
460 |
461 |
462 |
463 | Dependency:{" "}
464 |
465 | {item.dependency}
466 |
467 |
468 |
469 | Source:{" "}
470 | {item.source === "gemini" ? "Gemini AI" : "Local rules"}
471 |
472 |
473 |
474 |
475 | {item.createdAt}
476 |
477 |
478 | ))}
479 |
480 | )}
481 |
482 |
483 |
484 | );
485 | }
486 |
--------------------------------------------------------------------------------