# Project export: Clarifyd

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: TreeHacks 2026
- Tagline: Lost in lecture but too anxious to ask? Language barrier or speech impediment? If speaking up is hard, Clarifyd helps. Fall behind in seconds, catch up in one question.
- Devpost: https://devpost.com/software/clarifyd
- GitHub: https://github.com/ekagra1602/Clarifyd
- Team: 5 GitHub contributor(s) — ekagra1602 (27 commits), s-pat6 (18 commits), Cursor (16 commits), Daryl-Echeazu (2 commits), Sam Lu (1 commits)

## Devpost submission (written by the team)

### Overview

A real-time lecture platform where students watch AI-generated videos, compete on quiz leaderboards, and get instant answers — all while the lecture is happening. Features AI Video Studio Students can generate short explainer videos on demand during a lecture. Hit "Generate from Current Lecture" and the AI turns recent transcript into a visual video via Google Veo 3.1, or type a custom prompt like "Show me how gravity works". Every prompt is optimized by Gemini before generation. Live Quizzes + Leaderboard Teachers launch AI-generated quizzes from the live transcript with one click. Students answer in real time, earn streaks for consecutive correct answers, and climb the session leaderboard. Scores, streaks, and rankings update live. AI Q&A Chat Students open a chat overlay and ask anything about the lecture. The AI answers grounded in the live transcript and any uploaded course materials, so responses are always relevant to what's actually being taught. Teachers can review, approve, and add follow-ups to AI answers. Personalized Profiles Students set their language, learning style, pace preference, and accessibility needs during onboarding. The AI adapts — translating for ESL students, simplifying for different learning styles, adjusting explanations based on pace. Customizable DiceBear avatars let every student pick a look that fits. Teacher Dashboard Teachers get a single-page command center: start/stop transcription, launch quizzes, view live quiz stats with per-choice distributions, browse the student question feed, run AI question summaries, upload class context (PDFs, slides, notes), share a QR join code, and export session notes when class ends. Session Notes After a session, both teachers and students can download AI-generated markdown summary notes covering the key topics from the lecture.

## README (from the GitHub repository)

# Clarifyd

A real-time lecture platform where students watch AI-generated videos, compete on quiz leaderboards, and get instant answers — all while the lecture is happening.

---

## Features

### AI Video Studio
Students can generate short explainer videos on demand during a lecture. Hit "Generate from Current Lecture" and the AI turns recent transcript into a visual video via **Google Veo 3.1**, or type a custom prompt like *"Show me how gravity works"*. Every prompt is optimized by Gemini before generation.

### Live Quizzes + Leaderboard
Teachers launch AI-generated quizzes from the live transcript with one click. Students answer in real time, earn streaks for consecutive correct answers, and climb the session leaderboard. Scores, streaks, and rankings update live.

### AI Q&A Chat
Students open a chat overlay and ask anything about the lecture. The AI answers grounded in the live transcript and any uploaded course materials, so responses are always relevant to what's actually being taught. Teachers can review, approve, and add follow-ups to AI answers.

### Personalized Profiles
Students set their language, learning style, pace preference, and accessibility needs during onboarding. The AI adapts — translating for ESL students, simplifying for different learning styles, adjusting explanations based on pace. Customizable DiceBear avatars let every student pick a look that fits.

### Teacher Dashboard
Teachers get a single-page command center: start/stop transcription, launch quizzes, view live quiz stats with per-choice distributions, browse the student question feed, run AI question summaries, upload class context (PDFs, slides, notes), share a QR join code, and export session notes when class ends.

### Session Notes
After a session, both teachers and students can download AI-generated markdown summary notes covering the key topics from the lecture.




## Detected evidence (automated analysis)

Indexed codebase: 111 recognized source files, 604 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- LangChain (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 128)

```
.claude/settings.local.json
.cta.json
.env.example
.github/workflows/deploy-convex.yml
.github/workflows/test.yml
.gitignore
.mcp.json
.vscode/settings.json
agent/convex_client.py
agent/demo_logs.py
agent/library.py
agent/process_question.py
agent/requirements.txt
agent/server.py
agent/student.py
agent/teacher.py
agent/test_integration.py
agent/test_mutations.py
agent/verify_agents.py
agent/verify_context_aware_chat.py
agent/verify_with_real_session.py
bun.lock
bunfig.toml
CLAUDE.md
convex.json
convex/ai/compression.test.ts
convex/ai/compression.ts
convex/ai/context.ts
convex/ai/prompts.test.ts
convex/ai/prompts.ts
convex/ai/service.ts
convex/ai/types.ts
convex/assemblyai.ts
convex/backend.test.ts
convex/fileParser.ts
convex/http.ts
convex/leaderboard.ts
convex/lostEvents.ts
convex/questions.ts
convex/quizzes.ts
convex/README.md
convex/schema.ts
convex/sessions.ts
convex/transcripts.ts
convex/tsconfig.json
convex/videos.ts
convex/videosNode.ts
convex/zoom.ts
e2e/fixtures/session.fixture.ts
e2e/helpers/wait-for-convex.ts
e2e/mocks/api-intercept.ts
e2e/mocks/claude.mock.ts
e2e/pages/home.page.ts
e2e/pages/join.page.ts
e2e/pages/student-session.page.ts
e2e/pages/teacher-index.page.ts
e2e/pages/teacher-session.page.ts
e2e/README.md
e2e/tests/lost-signal.spec.ts
e2e/tests/navigation.spec.ts
e2e/tests/qa-flow.spec.ts
e2e/tests/quiz-flow.spec.ts
e2e/tests/session-flow.spec.ts
install_deps.sh
package.json
playwright.config.ts
public/.assetsignore
public/manifest.json
public/robots.txt
README.md
scripts/bun-test-disabled.ts
scripts/ears/browserbase_zoom_captions.ts
scripts/ears/local_loopback_gemini.py
scripts/ears/zoom_posthoc.ts
SPEC.md
src/__tests__/components/JoinPage.test.ts
src/__tests__/components/LostSummaryPanel.test.ts
src/__tests__/components/QAPanel.test.ts
src/__tests__/components/QuestionSummaryPanel.test.ts
src/__tests__/components/QuizModal.test.ts
src/__tests__/components/StudentSessionPage.test.ts
src/__tests__/components/TeacherSessionPage.test.ts
src/__tests__/components/TranscriptionControls.test.ts
src/__tests__/testUtils.tsx
src/__tests__/utils/displayUtils.test.ts
src/__tests__/utils/quizUtils.test.ts
src/components/AvatarPreview.tsx
src/components/Header.tsx
src/components/LeaderboardModal.tsx
src/components/ProfileOnboardingModal.tsx
src/components/StudentQuestionFeed.tsx
src/components/TranscriptionControls.tsx
src/hooks/usePaginatedTranscript.ts
src/hooks/useRealtimeTranscription.ts
src/lib/avatarOptions.ts
src/router.tsx
src/routes/__root.tsx
src/routes/index.tsx
src/routes/join.tsx
src/routes/session.$sessionId.tsx
src/routes/teacher.index.tsx
src/routes/teacher.session.$sessionId.tsx
src/routes/teacher.tsx
src/routeTree.gen.ts
src/styles.css
src/test/setup.ts
src/utils/displayUtils.ts
src/utils/quizUtils.ts
start_agent.sh
start_all.sh
tests/integration/ai.context.integration.test.ts
tests/integration/ai.lost-summary.integration.test.ts
tests/integration/ai.quiz-generation.integration.test.ts
tests/integration/lostEvents.integration.test.ts
tests/integration/questions.integration.test.ts
tests/integration/quizzes.integration.test.ts
tests/integration/sessions.integration.test.ts
tests/integration/transcription.integration.test.ts
tests/integration/transcripts.integration.test.ts
tests/lostEvents.test.ts
[8 more files omitted for size]
```

### Dependencies

- agent/requirements.txt: anthropic, chromadb, convex, google-generativeai, langchain, langgraph, mcp, pydantic@==2.10.6, pydantic-settings, python-dotenv
- package.json: @convex-dev/react-query@0.1.0, @playwright/test@^1.57.0, @tailwindcss/vite@^4.0.6, @tanstack/devtools-vite@^0.3.11, @tanstack/react-devtools@^0.7.0, @tanstack/react-router@^1.132.0, @tanstack/react-router-devtools@^1.132.0, @tanstack/react-router-ssr-query@^1.131.7, @tanstack/react-start@^1.132.0, @tanstack/router-plugin@^1.132.0, @testing-library/dom@^10.4.0, @testing-library/jest-dom@^6.9.1, @testing-library/react@^16.2.0, @types/node@^22.10.2, @types/qrcode@^1.5.6, @types/react@^19.2.0, @types/react-dom@^19.2.0, @vitejs/plugin-react@^5.0.4, clsx@^2.1.1, concurrently@^9.0.0, convex@^1.31.7, convex-test@^0.0.36, framer-motion@^12.26.2, jsdom@^27.0.0, lucide-react@^0.561.0, mammoth@^1.11.0, nitro@npm:nitro-nightly@latest, officeparser@^6.0.4, pdfjs-dist@^5.4.530, qrcode@^1.5.4, react@^19.2.0, react-dom@^19.2.0, tailwind-merge@^3.4.0, tailwindcss@^4.0.6, typescript@^5.7.2, vite@^7.1.7, vite-tsconfig-paths@^6.0.2, vitest@^3.0.5, web-vitals@^5.1.0

### Recent commits (newest first)

- Remove sections from README.md
- components change
- components change
- latest
- removed assembly
- Merge branch 'spandan/stuff'
- stuff
- merged avatar
- Merge branch 'daryl' into main
- Design + name change
- Avatar: DiceBear styles, inclusive options, no labels under preview
- translation
- syntax bug
- fix duplicate keys
- Merge branch 'spandan/mcp-integration' of https://github.com/ekagra1602/treehacks-education into spandan/mcp-integration
- teacher icon
- teacher icon
- Fixed layout
- basic mcp integration with app
- basic mcp integration with app

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

### CLAUDE.md

```markdown
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Clarifyd is a Convex-powered real-time lecture engagement platform. Teachers start sessions with join codes, students join to view live transcripts, ask AI questions, and take quizzes.

## Technology Stack

- **Backend**: Convex (real-time database, mutations/queries, no custom WebSocket needed)
- **Frontend**: React (Teacher Console + Student UI)
- **Transcription**: AssemblyAI real-time streaming (browser → AssemblyAI WebSocket → Convex mutation)
- **AI**: Claude Sonnet 4 for Q&A, quiz generation, and session notes

## Architecture

The system uses Convex's real-time subscriptions for all live updates. Clients subscribe to queries; Convex handles sync automatically when mutations write data.

### Transcription Flow

```
┌─────────────────┐    1. Get token      ┌─────────────────┐
│     Browser     │ ──────────────────▶  │  Convex Action  │
│  (React + mic)  │ ◀──────────────────  │  (validates     │
│                 │    (temp token)      │   session)      │
└────────┬────────┘                      └─────────────────┘
         │ 2. Connect with token
         ▼
┌─────────────────────────────────────┐
│  AssemblyAI WebSocket               │
│  wss://api.assemblyai.com/v2/...    │
│                                     │
│  - Sends audio chunks (PCM 16kHz)   │
│  - Receives partial/final transcripts│
└────────┬────────────────────────────┘
         │ 3. Save final transcripts
         ▼
┌─────────────────┐
│  Convex Mutation│  ← Direct mutation (no shared secret)
│  (validates     │
│   session live) │
└─────────────────┘
```

### Data Model (7 tables)

- `sessions` - Lecture sessions with join codes, status, contextText, activeQuizId
- `transcriptLines` - **Append-only** transcript segments (critical for real-time performance)
- `quizzes` - Quiz definitions with MCQ questions array
- `quizResponses` - Student answers to quizzes
- `lostEvents` - Historical signal events (deprecated in UI)
- `students` - Joined students with presence heartbeats and AI-generated summaries
- `questions` - Student Q&A pairs with AI responses

### Key Design Decisions

1. **Append-only transcripts**: Never update existing lines; always append new ones
2. **Real-time via Convex queries**: No custom WebSocket implementation
3. **Context building for AI**: Combine slides + recent transcript + Q&A for grounded responses
4. **Fallback strategies**: Quiz generation, STT, and AI responses all need graceful degradation
5. **Browser-direct transcription**: No agent infrastructure; browser connects directly to AssemblyAI

### AI Service Architecture

The AI system lives in `convex/ai/` with these components:
- `service.ts` - Unified Claude API wrapper for all AI features
- `prompts.ts` - System prompts and prompt builders for each feature type
- `compression.ts` - Token Compa
[truncated — 2409 more characters]
```

### SPEC.md

```markdown
# Clarifyd — Technical Specification

A Convex-powered real-time lecture engagement platform.

## 1. Overview

### 1.1 Purpose

Clarifyd enables real-time student engagement during live lectures through:
- Live transcription display
- AI-powered Q&A grounded in lecture content
- Teacher-triggered comprehension quizzes (CFU)
- "I'm lost" signals with spike detection

### 1.2 Success Criteria (POC Demo)

The following flow must work end-to-end:
1. Teacher starts session → join code appears
2. Students join → transcript updates live
3. Student asks AI question → receives grounded response
4. Teacher launches quiz → students answer
5. Teacher sees quiz accuracy update in real-time
6. Students hit "lost" → teacher sees spike indicator

---

## 2. User Roles

| Role | Capabilities |
|------|-------------|
| **Teacher** | Start session, upload slides, trigger quizzes, view insights (quiz stats, lost spikes) |
| **Student** | Join via code, view transcript, ask AI questions, take quizzes, mark "I'm lost" |

---

## 3. Architecture

### 3.1 System Components

```
┌─────────────────┐     ┌─────────────────┐
│  Teacher UI     │     │   Student UI    │
└────────┬────────┘     └────────┬────────┘
         │                       │
         └───────────┬───────────┘
                     │
              ┌──────▼──────┐
              │   Convex    │
              │  Backend    │
              └──────┬──────┘
                     │
         ┌───────────┴───────────┐
         │                       │
┌────────▼────────┐    ┌────────▼────────┐
│  Transcription  │    │   LLM Provider  │
│    Producer     │    │  (Q&A + Quiz)   │
└─────────────────┘    └─────────────────┘
```

### 3.2 Technology Stack

- **Backend**: Convex (single source of truth + real-time updates)
- **Frontend**: React (Teacher Console + Student UI)
- **Transcription**: AssemblyAI Universal Streaming v3 (browser → WebSocket → Convex mutation)
- **AI**: Claude Sonnet 4 for Q&A, quiz generation, lost summaries, and session notes

### 3.3 Real-time Strategy

Clients subscribe to Convex queries. No custom WebSocket implementation required—Convex handles real-time updates automatically when mutations write new data.

---

## 4. Data Model

### 4.1 Tables

#### `sessions`
| Field | Type | Description |
|-------|------|-------------|
| `_id` | ID | Primary key |
| `code` | string | Unique, human-readable join code (e.g., "blue-tiger") |
| `status` | "live" \| "ended" | Session state |
| `createdAt` | number | Timestamp |
| `contextText` | string? | Uploaded slides/context |
| `activeQuizId` | ID? | Currently active quiz |

#### `transcriptLines`
| Field | Type | Description |
|-------|------|-------------|
| `_id` | ID | Primary key |
| `sessionId` | ID | Foreign key to sessions |
| `text` | string | Transcript segment |
| `createdAt` | number | Timestamp |

> **Note**: Append-only design avoids conflicts and 
[truncated — 9312 more characters]
```

### package.json

```
{
  "name": "clarifyd",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "concurrently \"npm run dev:convex\" \"npm run dev:frontend\" \"npm run dev:agent\"",
    "dev:convex": "npx convex dev",
    "dev:frontend": "vite dev --port 3000",
    "dev:agent": "sh ./start_agent.sh",
    "dev:all": "concurrently \"npm run dev:convex\" \"npm run dev:frontend\" \"npm run dev:agent\"",
    "install:all": "sh ./install_deps.sh",
    "build": "vite build",
    "build:cloudflare": "NITRO_PRESET=cloudflare_pages vite build",
    "preview": "vite preview",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "test:e2e:mobile": "MOBILE=true playwright test",
    "test:e2e:headed": "playwright test --headed --workers=1",
    "test:e2e:ui": "playwright test --ui",
    "test:e2e:report": "playwright show-report",
    "build:production": "npx convex deploy --cmd 'vite build'"
  },
  "dependencies": {
    "@convex-dev/react-query": "0.1.0",
    "@tailwindcss/vite": "^4.0.6",
    "@tanstack/react-devtools": "^0.7.0",
    "@tanstack/react-router": "^1.132.0",
    "@tanstack/react-router-devtools": "^1.132.0",
    "@tanstack/react-router-ssr-query": "^1.131.7",
    "@tanstack/react-start": "^1.132.0",
    "@tanstack/router-plugin": "^1.132.0",
    "@types/qrcode": "^1.5.6",
    "@vitejs/plugin-react": "^5.0.4",
    "clsx": "^2.1.1",
    "convex": "^1.31.7",
    "framer-motion": "^12.26.2",
    "lucide-react": "^0.561.0",
    "mammoth": "^1.11.0",
    "nitro": "npm:nitro-nightly@latest",
    "officeparser": "^6.0.4",
    "pdfjs-dist": "^5.4.530",
    "qrcode": "^1.5.4",
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "tailwind-merge": "^3.4.0",
    "tailwindcss": "^4.0.6",
    "vite": "^7.1.7",
    "vite-tsconfig-paths": "^6.0.2"
  },
  "devDependencies": {
    "@playwright/test": "^1.57.0",
    "@tanstack/devtools-vite": "^0.3.11",
    "@testing-library/dom": "^10.4.0",
    "@testing-library/jest-dom": "^6.9.1",
    "@testing-library/react": "^16.2.0",
    "@types/node": "^22.10.2",
    "@types/react": "^19.2.0",
    "@types/react-dom": "^19.2.0",
    "concurrently": "^9.0.0",
    "convex-test": "^0.0.36",
    "jsdom": "^27.0.0",
    "typescript": "^5.7.2",
    "vitest": "^3.0.5",
    "web-vitals": "^5.1.0"
  }
}
```

### agent/requirements.txt

```
mcp
langchain
langgraph
convex
chromadb
google-generativeai
python-dotenv
pydantic==2.10.6
pydantic-settings
anthropic

```

### agent/server.py

```python
import os
from typing import List, Optional
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Initialize FastMCP server
mcp = FastMCP("TreeHacks Education Agent")

# Initialize Convex Client
try:
    from convex_client import client as convex_client
except ImportError:
    convex_client = None
    print("Warning: Could not import convex_client. Ensure agent/convex_client.py exists.")

# --- Models ---

class StudentProfile(BaseModel):
    student_id: str
    language: str = Field(description="ISO 639-1 language code (e.g., 'en', 'es')")
    disability: Optional[str] = Field(description="Type of disability, e.g., 'auditory', 'visual', or None")

class TranscriptChunk(BaseModel):
    text: str
    timestamp: float

# --- Tools ---

@mcp.tool()
def process_transcript_for_student(profile: StudentProfile, chunk: TranscriptChunk) -> str:
    """
    Processes a transcript chunk for a specific student based on their profile.
    Handles translation and simplification.
    """
    print(f"[AGENT: process_transcript_for_student] Processing chunk for student {profile.student_id} (Language: {profile.language}, Disability: {profile.disability})")
    processed_text = chunk.text
    
    # Placeholder for LangGraph logic
    # In a real implementation, this would invoke the student agent graph
    
    if profile.language != 'en':
        processed_text = f"[Translated to {profile.language}] {processed_text}"
        
    if profile.disability == 'auditory':
        processed_text = f"[Simplified] {processed_text}"
        
    return processed_text

@mcp.tool()
def anonymize_question(student_id: str, question: str) -> str:
    """
    Anonymizes a student's question before sending it to the database.
    """
    print(f"[AGENT: anonymize_question] Anonymizing question for student {student_id}")
    # Simple PII removal (placeholder)
    # In production, use an LLM or specialized library (Presidio)
    return question.replace(student_id, "STUDENT")

@mcp.tool()
def generate_teacher_insights(recent_questions: List[str]) -> str:
    """
    Analyzes recent student questions to generate insights for the teacher.
    Clusters similar questions.
    """
    print(f"[AGENT: generate_teacher_insights] Analyzing {len(recent_questions)} recent questions")
    if not recent_questions:
        return "No recent questions to analyze."
        
    # In a real scenario, we'd fetch from Convex if not provided, 
    # but here we process the list provided by the tool call (which might come from the frontend or another tool).
    # If the list is empty/placeholder strings, we can try to fetch from Convex.
    
    if len(recent_questions) == 1 and recent_questions[0].startswith("FETCH_FROM_CONVEX:"):
        # Protocol: If the argument is a specific string, fetch from DB
        session_id = recent_questions[0].split(":")[1]
        try:
            # Assuming we have a query `api.questions.listRecentQuestions`
            # We need to map this to the actual Convex query
            questions_data = convex_client.query("questions:listRecentQuestions", {"sessionId": session_id, "limit": 10})
            real_questions = [q["question"] for q in questions_data]
            if not real_questions:
                 return f"No questions found in session {session_id}."
            
            # Fetch context as well
            context_data = convex_client.query("sessions:getSessionContext", {"sessionId": session_id})
            uploaded_context = context_data.get("uploadedContext", "") if context_data else ""
            
            context_info = f" (Context available: {len(uploaded_context)} chars)" if uploaded_context else ""
            
            # Simple clustering simulation
            return f"Analyzed {len(real_questions)} questions from session {session_id}{context_info}. Common theme: {real_questions[0]}..."
        except Exception as e:
            return f"Error fetching questions: {str(e)}"

    return f"Cluster: {len(recent_questions)} questions provided. Topics appear varied."

@mcp.tool()
def check_library_for_answer(question: str) -> Optional[str]:
    """
    Checks the central library (Vector DB) if a similar question has been asked before.
    """
    print(f"[AGENT: check_library_for_answer] Checking library for question: {question}")
    # Placeholder for RAG logic
    return None

@mcp.tool()
def launch_auto_quiz(session_id: str, topic: str = "general", difficulty: str = "medium") -> str:
    """
    Launches an AI-generated quiz for the session.
    """
    print(f"[AGENT: launch_auto_quiz] Launching quiz for session {session_id} (Topic: {topic}, Difficulty: {difficulty})")
    if not convex_client:
        return "Error: Convex client not connected."
        
    try:
        # Map generic difficulty to specific literal if needed, or rely on type checking
        # api.quizzes.generateAndLaunchQuiz takes { sessionId, questionCount, difficulty }
        
        # We need to pass the ID as a string, Convex client handles serialization usually, 
        # but pure string might need to be wrapped if strictly typed on client side? 
        # The python client usually handles "string" -> ID coercion for arguments.
        
        result = convex_client.mutation("quizzes:generateAndLaunchQuiz", {
            "sessionId": session_id,
            "difficulty": difficulty if difficulty in ["easy", "medium", "hard"] else "medium"
        })
        return f"Quiz launched successfully! scheduled={result.get('scheduled')}"
    except Exception as e:
        return f"Failed to launch quiz: {str(e)}"

@mcp.tool()
def submit_ai_answer(question_id: str, answer: str) -> str:
    """
    Submits an AI-generated answer to a specific question.
    """
    print(f"[AGENT: submit_ai_answer] Submitting answer for question {question_id}")
    if not convex_client:
         return "Error: Convex client not con
[truncated — 324 more characters]
```

### src/routes/index.tsx

```typescript
import { createFileRoute, Link } from "@tanstack/react-router";
import { Users, GraduationCap, ArrowUpRight, Zap, Radio, BrainCircuit } from "lucide-react";
import { motion } from "framer-motion";

export const Route = createFileRoute("/")({ component: HomePage });

function HomePage() {
  return (
    <div className="min-h-screen flex flex-col items-center justify-center p-6 relative overflow-hidden bg-aurora bg-grid">
      {/* Ambient orbs */}
      <motion.div
        animate={{ y: [0, -30, 0], opacity: [0.4, 0.6, 0.4] }}
        transition={{ duration: 8, repeat: Infinity, ease: "easeInOut" }}
        className="absolute top-1/4 right-1/4 w-72 h-72 bg-primary/10 rounded-full blur-[100px] -z-10"
      />
      <motion.div
        animate={{ y: [0, 20, 0], opacity: [0.3, 0.5, 0.3] }}
        transition={{ duration: 10, repeat: Infinity, ease: "easeInOut" }}
        className="absolute bottom-1/4 left-1/4 w-96 h-96 bg-secondary/8 rounded-full blur-[120px] -z-10"
      />

      <div className="max-w-4xl w-full text-center mb-16 relative">
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.6 }}
        >
          <div className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-primary/10 border border-primary/20 mb-8">
            <Zap className="w-3.5 h-3.5 text-primary-light" />
            <span className="text-xs font-medium text-primary-light tracking-wide">Real-time lecture engagement</span>
          </div>
        </motion.div>

        <motion.h1
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.6, delay: 0.1 }}
          className="text-5xl md:text-7xl font-display font-bold text-text-primary mb-6 tracking-tight leading-[1.1]"
        >
          Lectures that
          <br />
          <span className="bg-gradient-to-r from-primary-light via-secondary to-primary-light bg-clip-text text-transparent">
            connect
          </span>
        </motion.h1>

        <motion.p
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.6, delay: 0.2 }}
          className="text-lg text-text-secondary max-w-lg mx-auto leading-relaxed"
        >
          Live transcription, AI-powered Q&A, instant quizzes.
          Everything your classroom needs, in one place.
        </motion.p>
      </div>

      <motion.div
        initial={{ opacity: 0, y: 30 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.6, delay: 0.35 }}
        className="max-w-3xl w-full grid grid-cols-1 md:grid-cols-2 gap-5 relative z-10"
      >
        {/* Student Card */}
        <Link to="/join" className="group">
          <div className="h-full card-glass-hover p-7 flex flex-col relative overflow-hidden">
            <div className="absolute top-0 right-0 w-40 h-40 bg-secondary/5 rounded-bl-full -z-0" />

            <div className="w-12 h-12 rounded-xl bg-secondary/15 border border-secondary/20 flex items-center justify-center mb-5 group-hover:bg-secondary/25 group-hover:shadow-glow-secondary transition-all">
              <Users className="w-5 h-5 text-secondary-light" />
            </div>

            <h2 className="text-xl font-display font-bold text-text-primary mb-2 relative z-10">
              Join a Session
            </h2>
            <p className="text-text-muted text-sm leading-relaxed mb-8 flex-grow relative z-10">
              Enter your class code to join the live lecture, ask questions, and take quizzes.
            </p>

            <div className="self-start px-5 py-2.5 rounded-lg bg-secondary/15 text-secondary-light text-sm font-semibold flex items-center gap-2 group-hover:bg-secondary/25 transition-all">
              Join Now <ArrowUpRight className="w-3.5 h-3.5" />
            </div>
          </div>
        </Link>

        {/* Teacher Card */}
        <Link to="/teacher" className="group">
          <div className="h-full card-glass-hover p-7 flex flex-col relative overflow-hidden">
            <div className="absolute top-0 right-0 w-40 h-40 bg-primary/5 rounded-bl-full -z-0" />

            <div className="w-12 h-12 rounded-xl bg-primary/15 border border-primary/20 flex items-center justify-center mb-5 group-hover:bg-primary/25 group-hover:shadow-glow transition-all">
              <GraduationCap className="w-5 h-5 text-primary-light" />
            </div>

            <h2 className="text-xl font-display font-bold text-text-primary mb-2 relative z-10">
              Start a Class
            </h2>
            <p className="text-text-muted text-sm leading-relaxed mb-8 flex-grow relative z-10">
              Launch a live session with transcription, quizzes, and real-time student engagement.
            </p>

            <div className="self-start px-5 py-2.5 rounded-lg bg-primary/15 text-primary-light text-sm font-semibold flex items-center gap-2 group-hover:bg-primary/25 transition-all">
              Get Started <ArrowUpRight className="w-3.5 h-3.5" />
            </div>
          </div>
        </Link>
      </motion.div>

      {/* Feature pills */}
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 0.6, delay: 0.55 }}
        className="mt-16 flex flex-wrap items-center justify-center gap-3"
      >
        {[
          { icon: Radio, label: "Live Transcription" },
          { icon: BrainCircuit, label: "AI Q&A" },
          { icon: Zap, label: "Instant Quizzes" },
        ].map(({ icon: Icon, label }) => (
          <div key={label} className="flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-bg-card border border-border text-text-muted text-xs font-medium">
            <Icon className="w-3 h-3" />
            {label}
          </div>
        ))}
      </motion.div>
    </div>
  );
}

```

### start_agent.sh

```shell

#!/bin/bash

# Ensure dependencies are installed
echo "Installing/Checking Python dependencies..."
pip install -r agent/requirements.txt

# Start the Python Agent
echo "Starting Python Agent for Q&A..."
python agent/process_question.py

```

### install_deps.sh

```shell
#!/bin/bash

echo "Installing all dependencies..."

# 1. Install Node modules
echo "→ Installing Node modules..."
npm install

# 2. Install Python dependencies
echo "→ Installing Python dependencies..."
pip install -r agent/requirements.txt

echo "✅ All dependencies installed successfully!"

```

### vitest.backend.config.ts

```typescript
import { defineConfig } from 'vitest/config';
import viteTsConfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  plugins: [viteTsConfigPaths()],
  test: {
    environment: 'node',
    include: ['convex/backend.test.ts'],
    // setupFiles: [], // Explicitly empty to avoid picking up src/test/setup.ts if inherited
  },
});

```

### vitest.config.ts

```typescript
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  test: {
    environment: "jsdom",
    setupFiles: ["./src/test/setup.ts"],
    include: ["src/**/*.test.{ts,tsx}", "convex/**/*.test.ts", "tests/**/*.test.ts"],
    globals: true,
    // Run tests in forked processes to isolate unhandled errors from scheduled functions
    pool: "forks",
  },
});

```

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