# Project export: Learnr

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: Change how you learn forever
- Devpost: https://devpost.com/software/learnr-x8bpqd
- GitHub: https://github.com/DevoutSomething/CalHacks2025
- Video: https://www.youtube.com/embed/wfLoJB1EK-Q?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Archer L Fox (33 commits), lukeeskinner (18 commits), Khai Nguyen (2 commits), ajauni01 (1 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

# 🧠 Learnr – Learn Smarter, Not Harder

> Experience the future of education with AI-powered interactive learning.  
> Visualize concepts, take quizzes, and master any subject with personalized guidance.


## 🚀 Overview

**Learnr** is an intelligent education platform built using the **MERN stack** and enhanced by **Python-powered AI video generation**.  
It helps learners master topics through **multiple learning styles** — interactive text, adaptive quizzes, and visual explanations — powered by cutting-edge AI tools like **Groq** and **Manim**.

Our mission is simple: **make learning faster, deeper, and more intuitive**.

---

## ✨ Features

### 🧩 Interactive Learning
Engage with dynamically generated explanations, examples, and guided exercises tailored to your knowledge level.

### 🧠 Smart Quizzes
Instantly test your understanding with AI-generated quizzes that adapt in real time based on your progress and performance.

### 🎥 Visual Concepts
Visualize abstract ideas with automatically generated **Manim** videos — rendered using **Groq** for ultra-fast computation.

### 🗣️ Multi-Style Learning
Choose your preferred learning mode — **visual**, **theoretical**, **interactive**, or **quiz-based** — and switch seamlessly at any time.

---

## 🏗️ Tech Stack

| Layer | Technology | Description |
|-------|-------------|--------------|
| **Frontend** | React, Vite, TailwindCSS | Responsive and dynamic UI for lessons, videos, and quizzes |
| **Backend** | Node.js, Express | REST API, authentication, and quiz logic |
| **Database** | MongoDB Atlas | Stores user data, progress, and content metadata |
| **AI / Video Engine** | Python, Groq, Manim | Generates AI-driven educational videos |
| **Integration** | REST + WebSocket | Real-time session updates and data streaming |

---

## 🧮 System Architecture



## Detected evidence (automated analysis)

Indexed codebase: 53 recognized source files, 211 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Firebase (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- MongoDB (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (63 of 63)

```
.gitignore
Backend/config/vapiConfig.js
Backend/lukeadd.js
Backend/models/User.js
Backend/package.json
Backend/prompt.txt
Backend/scripts/manim_script_1761423875342.py
Backend/server.js
Backend/uploads/898e215c69bb675db6d5982c963ccc96
frontend/components.json
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/postcss.config.js
frontend/README.md
frontend/src/App.css
frontend/src/App.tsx
frontend/src/components/Header.tsx
frontend/src/components/LatexText.tsx
frontend/src/components/QuizForm.tsx
frontend/src/components/SignInButton.tsx
frontend/src/components/TabularResults.tsx
frontend/src/components/TextResult.tsx
frontend/src/components/TextSubmit.tsx
frontend/src/components/UserSettingsForm.css
frontend/src/components/UserSettingsForm.tsx
frontend/src/components/VapiAudioInterface.css
frontend/src/components/VapiAudioInterface.tsx
frontend/src/components/VideoTab.css
frontend/src/components/VideoTab.tsx
frontend/src/components/Whiteboard.css
frontend/src/components/Whiteboard.tsx
frontend/src/contexts/UserSettingsContext.tsx
frontend/src/firebase.ts
frontend/src/index.css
frontend/src/lib/utils.ts
frontend/src/main.tsx
frontend/src/pages/Home.tsx
frontend/src/pages/Landing.tsx
frontend/src/pages/Learn.tsx
frontend/src/pages/Result.tsx
frontend/src/pages/Video.css
frontend/src/pages/Video.tsx
frontend/styles/Header.css
frontend/styles/Landing.css
frontend/styles/LatexText.css
frontend/styles/QuizForm.css
frontend/styles/Result.css
frontend/styles/TabularResults.css
frontend/styles/TextSubmit.css
frontend/styles/Whiteboard.css
frontend/tailwind.config.js
frontend/tsconfig.app.json
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/tsconfig.tsbuildinfo
frontend/vite.config.ts
README.md
VAPI_ADVANCED_CONFIG.md
VAPI_HUMAN_ENHANCEMENTS.md
VAPI_SETUP_GUIDE.md
VAPI_UPDATE_SUMMARY.md
WHITEBOARD_AI_GUIDE.md
```

### Dependencies

- Backend/package.json: cors@^2.8.5, dotenv@^16.4.5, express@^5.1.0, expresss@^0.0.0, ffmpeg-static@^5.2.0, groq-sdk@^0.7.0, mongoose@^8.19.2, multer@^1.4.5-lts.1, nodemon@^3.1.10
- frontend/package.json: @eslint/js@^9.36.0, @excalidraw/excalidraw@^0.18.0, @types/node@^24.6.0, @types/react@^18.3.26, @types/react-dom@^18.3.7, @vitejs/plugin-react@^5.0.4, dotenv@^17.2.3, eslint@^9.36.0, eslint-plugin-react-hooks@^5.2.0, eslint-plugin-react-refresh@^0.4.22, firebase@^12.4.0, globals@^16.4.0, katex@^0.16.25, react@^18.3.1, react-dom@^18.3.1, react-katex@^3.0.1, react-markdown@^10.1.0, react-router-dom@^7.9.4, rehype-katex@^7.0.1, remark-gfm@^4.0.1, remark-math@^6.0.0, typescript@~5.9.3, typescript-eslint@^8.45.0, vite@^7.1.7

### Recent commits (newest first)

- Update README.md
- Create README.md
- vapi api
- header fix
- more cleanup
- Ui fixes
- UI changes
- Merged some things
- fixes
- async video generation
- added video cache
- new prompt
- Whiteboard new setup and server add on
- Whiteboard fix!!
- Fix stuff
- fix again
- Merge branch 'main' of https://github.com/DevoutSomething/Hackathon2025
- Fixed the video gen
- Merge branch 'main' of https://github.com/DevoutSomething/CalHacks2025
- Working whiteboard

## Key source files (fetched from GitHub, selected and truncated for size)

### VAPI_UPDATE_SUMMARY.md

```markdown
# Vapi AI Update Summary - Available for All Learners! 🎉

## What Changed?

The Vapi AI voice learning feature is now **available for ALL learning styles**, not just auditory learners!

---

## 🎯 Key Improvements

### 1. **Universal Access**
- ✅ All learners (Visual, Auditory, Kinesthetic, Reading/Writing) can now use voice learning
- ✅ "🎧 Voice Learning" button appears for everyone in the tab navigation
- ✅ Each learner gets a customized teaching approach via voice

### 2. **Smart Auto-Open Behavior**
- **Auditory Learners**: Voice interface opens automatically (because voice is their primary preference)
- **Other Learners**: Voice interface available via button click (manual trigger)

### 3. **Adaptive Teaching Styles**
The AI now adapts its teaching approach based on learning style:

#### 🔊 Auditory Learners
- Conversational, storytelling approach
- Rhythm and variation in speech
- Verbal cues and repetition
- Engaging, spoken-style explanations

#### 👁️ Visual Learners  
- Descriptive visual imagery
- "Picture this...", "Imagine..." language
- Describes diagrams and spatial relationships
- Encourages mental visualization and sketching

#### ✋ Kinesthetic Learners
- Practical, hands-on examples
- Action-oriented instructions
- Physical metaphors and real-world practice
- Interactive, "try this now" approach

#### 📝 Reading/Writing Learners
- Structured, organized explanations
- Clear definitions and key terms
- Logical progression with note-taking cues
- Academic, precise language

### 4. **Personalized Greetings**
Each learning style gets a custom greeting:

- **Auditory**: "...I heard you are an auditory learner..."
- **Visual**: "...I heard you are a visual learner. While I'll be teaching through voice, I'll describe concepts in a visual way that helps you picture them..."
- **Kinesthetic**: "...I heard you are a kinesthetic learner. I'll give you practical examples you can work through..."
- **Reading/Writing**: "...I heard you are a reading/writing learner. I'll structure the lesson clearly..."

---

## 📝 Files Modified

### Frontend:
1. **`/frontend/src/components/VapiAudioInterface.tsx`**
   - Added user settings context
   - Passes learning style to backend

2. **`/frontend/src/components/TabularResults.tsx`**
   - Removed learning style restrictions
   - Voice button now visible for all learners
   - Auto-show only triggers for auditory learners

### Backend:
1. **`/Backend/server.js`**
   - Accepts `learningStyle` parameter
   - Creates style-specific greetings
   - Passes learning style to system prompt

2. **`/Backend/config/vapiConfig.js`**
   - Complete rewrite with adaptive teaching styles
   - Four unique teaching approaches
   - Style-specific guidelines for the AI

### Documentation:
1. **`/VAPI_SETUP_GUIDE.md`**
   - Updated testing instructions
   - Added separate flows for each learner type
   - Expanded testing checklist

2. **`/VAPI_UPDATE_SUMMARY.md`** (this file)
   - Change summary and overview

---

## 🚀 How 
[truncated — 3675 more characters]
```

### VAPI_SETUP_GUIDE.md

```markdown
# Vapi AI Audio Learning - Setup & Testing Guide

## 🎉 Implementation Complete!

All code has been implemented for the Vapi AI auditory learning feature. Here's what you need to do to test it.

---

## 📝 Environment Variables Required

### Backend `.env` file
Add this to `/Backend/.env`:
```bash
VAPI_PRIVATE_KEY=your_vapi_private_key_here
```

### Frontend `.env` file
Create `/frontend/.env` and add:
```bash
VITE_VAPI_PUBLIC_KEY=your_vapi_public_key_here
VITE_API_URL=http://localhost:3000
```

---

## 🚀 How to Test

### 1. Start the Backend
```bash
cd Backend
npm start
```

### 2. Start the Frontend
```bash
cd frontend
npm run dev
```

### 3. Test the Feature

#### For Auditory Learners (Auto-opens):
1. **Navigate to the learning page** (`/learn`)
2. **Select "Auditory Learner"** from the Learning Style dropdown
3. **Enter a subject** (e.g., "Quantum Physics", "JavaScript Promises", "World War II")
4. **Submit your query**
5. **The Vapi interface will automatically appear** with a beautiful modal
6. **Click "Start Learning Session"** to begin the voice conversation
7. **The AI will greet you and ask for confirmation**
8. **Respond with "yes" or "sounds good"** to confirm
9. **The AI will begin teaching** using auditory-optimized explanations

#### For Other Learners (Manual trigger):
1. **Navigate to the learning page** (`/learn`)
2. **Select any learning style** (Visual, Kinesthetic, or Reading/Writing)
3. **Enter a subject** and submit
4. **You'll see the "🎧 Voice Learning" button** in the tab navigation
5. **Click it to open the voice learning interface**
6. **The AI will adapt its teaching style** to your learning preference:
   - **Visual**: Describes concepts with vivid visual imagery
   - **Kinesthetic**: Focuses on practical, hands-on examples
   - **Reading/Writing**: Provides structured, note-worthy explanations

---

## ✨ Features Implemented

### 1. **VapiAudioInterface Component** (`/frontend/src/components/VapiAudioInterface.tsx`)
   - Beautiful modal interface with gradient design
   - Real-time audio visualization with pulse effects
   - Live transcript display
   - Mute/unmute controls
   - Connection status indicators
   - Smooth animations and transitions

### 2. **Backend API Endpoint** (`/Backend/server.js`)
   - `/vapi/create-assistant` endpoint
   - Creates custom Vapi assistants with system prompts
   - Configures voice (ElevenLabs Rachel voice)
   - Sets up Deepgram transcriber

### 3. **Vapi Configuration** (`/Backend/config/vapiConfig.js`)
   - Custom system prompt for auditory learners
   - Conversation flow with confirmation logic
   - Teaching style optimized for spoken learning

### 4. **TabularResults Integration** (`/frontend/src/components/TabularResults.tsx`)
   - Auto-detects auditory learner preference
   - Shows "🎧 Voice Learning" button
   - Auto-launches Vapi interface for auditory learners
   - Seamless integration with existing tabs

### 5. **Styling** (`/frontend/src/components/VapiAudioInterface.cs
[truncated — 5843 more characters]
```

### Backend/package.json

```
{
  "name": "backend",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^5.1.0",
    "expresss": "^0.0.0",
    "ffmpeg-static": "^5.2.0",
    "groq-sdk": "^0.7.0",
    "mongoose": "^8.19.2",
    "multer": "^1.4.5-lts.1",
    "nodemon": "^3.1.10"
  }
}

```

### frontend/package.json

```
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@excalidraw/excalidraw": "^0.18.0",
    "dotenv": "^17.2.3",
    "firebase": "^12.4.0",
    "katex": "^0.16.25",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-katex": "^3.0.1",
    "react-markdown": "^10.1.0",
    "react-router-dom": "^7.9.4",
    "rehype-katex": "^7.0.1",
    "remark-gfm": "^4.0.1",
    "remark-math": "^6.0.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.36.0",
    "@types/node": "^24.6.0",
    "@types/react": "^18.3.26",
    "@types/react-dom": "^18.3.7",
    "@vitejs/plugin-react": "^5.0.4",
    "eslint": "^9.36.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.22",
    "globals": "^16.4.0",
    "typescript": "~5.9.3",
    "typescript-eslint": "^8.45.0",
    "vite": "^7.1.7"
  }
}

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { BrowserRouter } from "react-router-dom";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <BrowserRouter>
      {" "}
      <App />
    </BrowserRouter>
  </StrictMode>
);

```

### frontend/src/App.tsx

```typescript
import { Routes, Route } from "react-router-dom";
import "./App.css";
import "../styles/Landing.css";
import Learn from "./pages/Learn.tsx";
import Result from "./pages/Result.tsx";
import Landing from "./pages/Landing.tsx";
import Video from "./pages/Video.tsx";
import Header from "./components/Header";
import Whiteboard from "./components/Whiteboard";
import { UserSettingsProvider } from "./contexts/UserSettingsContext";

function App() {
  return (
    <UserSettingsProvider>
      <Routes>
        <Route path="/whiteboard" element={<Whiteboard />} />
        <Route path="/" element={
          <>
            <Header />
            <Landing />
          </>
        } />
        <Route path="/learn" element={
          <>
            <Header />
            <Learn />
          </>
        } />
        <Route path="/result" element={
          <>
            <Header />
            <Result />
          </>
        } />
        <Route path="/video" element={
          <>
            <Header />
            <Video />
          </>
        } />
      </Routes>
    </UserSettingsProvider>
  );
}

export default App;

```

### Backend/server.js

```javascript
require("dotenv").config();

const cors = require("cors");
const express = require("express");
const { Groq } = require("groq-sdk");
const multer = require("multer");
const fs = require("fs");
const path = require("path");
const { exec } = require("child_process");
const app = express();
const PORT = 3000;
const mongoose = require("mongoose");
const User = require("./models/User");

// Initialize Groq
const groq = new Groq({
  apiKey: process.env.GROQ_API_KEY,
});

// Middleware with increased limit for base64 images
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ extended: true, limit: '50mb' }));

// Configure multer for file uploads
const upload = multer({ dest: 'uploads/' });

// Serve static files from the videos directory
app.use('/videos', express.static(path.join(__dirname, 'videos')));

// CORS middleware
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept, Authorization"
  );
  if (req.method === "OPTIONS") {
    res.sendStatus(200);
  } else {
    next();
  }
});

// Function to call Claude API
async function callClaudeAPI(userPrompt) {
  console.log("Calling Claude API with prompt:", userPrompt);

  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
    
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.CLAUDE_API_KEY,
        'anthropic-version': '2023-06-01'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-5',
        max_tokens: 4096,
        messages: [
          {
            role: "user",
            content: userPrompt
          }
        ]
      }),
      signal: controller.signal
    });
    
    clearTimeout(timeoutId);

    if (!response.ok) {
      throw new Error(`Claude API error: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    console.log("Claude API Response:", data);
    return data.content[0]?.text || "";
  } catch (error) {
    console.error("Error calling Claude API:", error);
    if (error.name === 'AbortError') {
      throw new Error('Claude API request timed out after 30 seconds');
    } else if (error.code === 'UND_ERR_CONNECT_TIMEOUT') {
      throw new Error('Claude API connection timeout - please check your internet connection');
    } else {
      throw new Error(`Claude API error: ${error.message}`);
    }
  }
}
async function callClaudeAPIWithSystem(userPrompt, systemPrompt) {
  console.log("Calling Claude API with custom system prompt");

  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
    
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.CLAUDE_API_KEY,
        'anthropic-version': '2023-06-01'
      },
      body: JSON.stringify({
        model: 'claude-sonnet-4-5',
        max_tokens: 4096,
        system: systemPrompt,
        messages: [
          {
            role: "user",
            content: userPrompt
          }
        ]
      }),
      signal: controller.signal
    });
    
    clearTimeout(timeoutId);

    if (!response.ok) {
      throw new Error(`Claude API error: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    console.log("Claude API Response:", data);
    return data.content[0]?.text || "";
  } catch (error) {
    console.error("Error calling Claude API:", error);
    if (error.name === 'AbortError') {
      throw new Error('Claude API request timed out after 30 seconds');
    } else if (error.code === 'UND_ERR_CONNECT_TIMEOUT') {
      throw new Error('Claude API connection timeout - please check your internet connection');
    } else {
      throw new Error(`Claude API error: ${error.message}`);
    }
  }
}

// Function to call Groq API
async function callGroqAPI(userPrompt) {
  console.log("Calling Groq API with prompt:", userPrompt);

  try {
    const chatCompletion = await groq.chat.completions.create({
      messages: [
        {
          role: "system",
          content: `You are an educational AI assistant built to help students understand and visualize concepts interactively. 
Your job is to teach, not just tell. You adapt your explanations to the user's preferred learning style: visual, practical, or auditory.
When responding:
- Simplify complex ideas with examples, analogies, or diagrams.
- For **visual learners**, describe diagrams or sketches for the whiteboard.
- For **practical learners**, give example-based reasoning or real-world exercises.
- For **auditory learners**, explain in spoken-style language.
- Use LaTeX notation wrapped in $$ for math, e.g. $$x^2 + y^2 = r^2$$.
- Be concise, interactive, and encourage experimentation.
- Suggest actions like 'try sketching a parabola opening upward' when appropriate.
- If the user draws something, interpret or explain it.
- Focus on helping the user *understand*, not just giving answers. Dont use bullet points in the explanation, use paragraphs instead. 
- Help the user and dont focus on giving answers, give an explentaion that is easy to follow. dont ask the user any follow up questions
- you should not act as an LLM. Act as a teacher and do not thank or say anything related to the question. Simply give the explenation step by step`
        },
        {
          role: "user",
          content: userPrompt,
        },
      ],
      model: "llama-3.3-70b-versatile",
      temperature: 1,
      max_completion_tokens: 8192,
      top_p: 1,
      stream: false,
 
[truncated — 36565 more characters]
```

### frontend/vite.config.ts

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### frontend/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
    <title>frontend</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### frontend/eslint.config.js

```javascript
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
      reactHooks.configs['recommended-latest'],
      reactRefresh.configs.vite,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
])

```

[37 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]