# Project export: Study Transit

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: Students often lose time finding places to study on campus. StudyTransit is an AI study companion that recommends spots based on your preferences and real occupancy signals and tracks your momentum.
- Devpost: https://devpost.com/software/study-transit
- GitHub: https://github.com/zaryaabkhan/studytransit
- Video: https://www.youtube.com/embed/iEAqjPqXdiQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — BxNoel (7 commits), zaryaabkhan (5 commits), Cursor (4 commits)

## Devpost submission (written by the team)

### Overview

Find your flow. AI-powered study space discovery and focus sessions for students. Created by Noel Negron & Zaryaab Khan Built for TreeHacks 2026.

### Inspiration

We kept seeing students at Columbia wander between libraries, check crowdedness, and lose focus before actually studying. Its often that at school, especially during midterms, we spend almost 30 minutes looking for seats at school that actually sitting down and studying. We wanted to cut that friction: a single app to discover the right space, lock in with a focus timer, and track progress with streaks and exam prep. StudyTransit brings discovery, focus, and accountability into one flow, so you spend less time hunting and more time studying. What It Does StudyTransit helps University students discover the right study spaces, stay focused with a timer, and track progress with streaks and exam prep—all in one app. How We Built It React + Vite SPA with React Router. Libraries and study spaces are stored in Firebase (collection Libraries with subcollection spaces per library). Sessions, exams, ratings, and weekly goals live in LocalStorage. Claude is preferred for AI; OpenAI is fallback. PDF parsing uses pdfjs-dist, then Claude extracts exam dates and study topics. Discover uses getAISpaceRecommendations for AI-curated space matches (with rule-based demo fallback when no API key). Challenges We Ran Into Syllabus parsing – PDFs vary a lot. We used text extraction plus Claude to get exam dates and topics, with fallback "recommended" topics when parsing failed. Claude from the browser – CORS and direct-browser access required some setup. We iterated on prompts and models until AI responses were consistent. Scope – Balancing AI features (recommendations, syllabus parsing, insights, tips) with a simple UI. We cut or simplified features to keep the core flow clear. Accomplishments That We're Proud Of End-to-end flow – Discovery → Lock In → Stats, all working together. AI integration – Multiple AI touchpoints (recommendations, syllabus parsing, study tips, weekly insights) with fallbacks when the API key is missing. Exam workflow – Upload a PDF, get exams and study to-dos, and check them off as you go. Clean, themed UI – Columbia University branding with consistent footer nav and modals. What We Learned AI works well for structured tasks (parsing syllabi, summarizing stats, generating tips) when prompts are clear and outputs are constrained. LocalStorage is enough for a demo; sessions, exams, and ratings persist without a backend. Small UX choices (shorter demo timer, simpler reflection, no clutter) make the app feel calmer and easier to use. What's Next for StudyTransit Campus integration – Real library occupancy or space availability from schools outside of Columbia. Backend + auth – Sync sessions and exams across devices and users. Virtual study rooms – Zoom integration for one-click group study links. Mobile app – Native experience for on-the-go discovery and focus. Tracks We Are Applying To We are in the Education track (sponsor: Zoom). TreeHacks 2026 Education Grand Prize. StudyTransit fits the Education track by: Helping students find the right study spaces (libraries, quiet rooms, group areas) Lock In focus timer for productive study sessions Stats dashboard with streaks, exam deadlines, and AI-powered study tips Tech Stack Frontend: React 18, Vite 6, React Router 6 Data: Firebase Firestore (libraries, spaces, capacity) AI: Anthropic Claude (primary), OpenAI (fallback). Demo mode when no API key. PDF: pdfjs-dist for syllabus text extraction + AI for exam/topic parsing Charts: Recharts for space occupancy trends Persistence: LocalStorage for ratings, focus sessions, exams, goals Try It Open http://localhost:5173

## README (from the GitHub repository)

# StudyTransit

**Find your flow.** AI-powered study space discovery and focus sessions for students.

**Created by Noel Negron & Zaryaab Khan**

Built for [TreeHacks 2026](https://treehacks.com).

---

## Inspiration

We kept seeing students wander between libraries, check crowdedness, and lose focus before actually studying. We wanted to cut that friction: a single app to discover the right space, lock in with a focus timer, and track progress with streaks and exam prep. StudyTransit brings discovery, focus, and accountability into one flow, so you spend less time hunting and more time studying.

---

## What It Does

StudyTransit helps Columbia University students **discover the right study spaces**, **stay focused with a timer**, and **track progress** with streaks and exam prep—all in one app.

| Page | Description |
|------|-------------|
| **Home** | Browse Columbia libraries with live occupancy data. See "Best bet now" (least crowded library). Rate spaces (1–5) to contribute crowd-sourced data. |
| **Discover** | Set preferences: intensity (deep/steady/social), noise level (silent/busy/buzz), group size, session length. Describe needs in plain text. AI recommends 3–5 spaces with personalized reasons. |
| **Lock In** | Pick task type (exam/reading/writing/project), energy level, and your current space. Run a focus timer (10 sec–3 min). Rate your focus after each session and get AI coaching tips. |
| **Stats** | Weekly dashboard: study streak, focused minutes, completed sessions. Set weekly goals. Add exams manually or upload a syllabus PDF to extract dates and study topics. Get AI insights and exam-specific tips. |

---

## How We Built It

React + Vite SPA with React Router. Libraries and study spaces are stored in Firebase Firestore (collection `Libraries` with subcollection `spaces` per library); the app falls back to built-in Columbia data if Firebase is not configured. Sessions, exams, ratings, and weekly goals live in LocalStorage. Claude (Anthropic) is preferred for AI; OpenAI is fallback. In dev, Vite proxies `/api/anthropic` to avoid CORS. PDF parsing uses pdfjs-dist, then Claude extracts exam dates and study topics. Discover uses `getAISpaceRecommendations` for AI-curated space matches (with rule-based demo fallback when no API key).

---

## Challenges We Ran Into

- **Syllabus parsing** – PDFs vary a lot. We used text extraction plus Claude to get exam dates and topics, with fallback "recommended" topics when parsing failed.
- **Claude from the browser** – CORS and direct-browser access required some setup. We iterated on prompts and models until AI responses were consistent.
- **Scope** – Balancing AI features (recommendations, syllabus parsing, insights, tips) with a simple UI. We cut or simplified features to keep the core flow clear.

---

## Accomplishments That We're Proud Of

- **End-to-end flow** – Discovery → Lock In → Stats, all working together.
- **AI integration** – Multiple AI touchpoints (recommendations, syllabus parsing, study tips, weekly insights) with fallbacks when the API key is missing.
- **Exam workflow** – Upload a PDF, get exams and study to-dos, and check them off as you go.
- **Clean, themed UI** – Columbia University branding with consistent footer nav and modals.

---

## What We Learned

- AI works well for structured tasks (parsing syllabi, summarizing stats, generating tips) when prompts are clear and outputs are constrained.
- LocalStorage is enough for a demo; sessions, exams, and ratings persist without a backend.
- Small UX choices (shorter demo timer, simpler reflection, no clutter) make the app feel calmer and easier to use.

---

## What's Next for StudyTransit

- **Campus integration** – Real library occupancy or space availability from Columbia systems.
- **Backend + auth** – Sync sessions and exams across devices and users.
- **Virtual study rooms** – Zoom integration for one-click group study links.
- **Mobile app** – Native experience for on-the-go discovery and focus.

---

## Tracks We Are Applying To

**We are in the Education track** (sponsor: Zoom). TreeHacks 2026 Education Grand Prize awards the top study/learning apps that help students discover spaces, focus, and track progress.

StudyTransit fits the Education track by:
- Helping students find the right study spaces (libraries, quiet rooms, group areas)
- Lock In focus timer for productive study sessions
- Stats dashboard with streaks, exam deadlines, and AI-powered study tips

---

## Tech Stack

- **Frontend**: React 18, Vite 6, React Router 6
- **Data**: Firebase Firestore (libraries, spaces, capacity)
- **AI**: Anthropic Claude (primary), OpenAI (fallback). Demo mode when no API key.
- **PDF**: pdfjs-dist for syllabus text extraction + AI for exam/topic parsing
- **Charts**: Recharts for space occupancy trends
- **Persistence**: LocalStorage for ratings, focus sessions, exams, goals

---

## Project Structure

```
react-app/
├── src/
│   ├── App.jsx              # Routes + AppShell with footer nav
│   ├── main.jsx
│   ├── theme.css             # Columbia University theme (primary blues)
│   ├── styles.css
│   │
│   ├── pages/
│   │   ├── HomePage.jsx      # Library list, best bet, rating modal
│   │   ├── LibraryPage.jsx   # Spaces for one library (SpaceCard grid)
│   │   ├── DiscoverPage.jsx  # Preference form → AI recommendations
│   │   ├── LockPage.jsx     # Focus timer, reflection, AI tips
│   │   └── StatsPage.jsx    # Streaks, goals, exams, syllabus upload, AI insights
│   │
│   ├── components/
│   │   ├── SpaceCard.jsx    # Occupancy display, weekly chart, community avg
│   │   ├── FooterNav.jsx    # Home / Discover / Lock In / Stats
│   │   └── Logo.jsx
│   │
│   ├── state/
│   │   └── AppState.jsx     # Global state (libraries, spaces, ratings, sessions, exams)
│   │
│   ├── services/
│   │   ├── aiClient.js      # Claude/OpenAI: recommendations, coaching, syllabus parsing
│   │   ├── pdfParser.js     # Extract text from PDFs
│   │   └── firebase.js      # Firestore init (alternative to firebase/)
│   │
│   ├── firebase/
│   │   ├── firebase.js      # Firebase app + Firestore db
│   │   ├── firebase_utility.jsx  # fetchAllLibraries, fetchAllSpacesFromLibrary, updateSpaceCapacityAndCounter
│   │   └── seed_columbia_libraries.js.js  # Admin seed script (Libraries collection)
│   │
│   └── hooks/
│       └── useRecommender.js  # Rule-based scoring (noise, intensity, capacity)
│
├── scripts/
│   └── seed-firestore.js    # Prints flat libraries/spaces JSON (different schema)
├── vercel.json              # SPA rewrites for Vercel deploy
└── vite.config.js           # Proxy /api/anthropic → Anthropic API (keeps key server-side in dev)
```

---

## How It Works

### Libraries & Spaces (Firebase)

- **Collection**: `Libraries` (doc IDs: lib1, lib2, …)
- **Fields**: `library_name`, optional `location`
- **Subcollection**: `Libraries/{libraryId}/spaces` — each space has `room_data`:
  - `space_name`, `space_capacity`, `space_counter`
  - Day keys: `Monday`, `Tuesday`, … for weekly occupancy trends

The app loads libraries on mount, then fetches spaces per library. If Firebase is not configured, it falls back to hardcoded Columbia data in `AppState.jsx`.

### Occupancy & Ratings

- **space_capacity** / **space_counter** → ratio (capacity ÷ people rated) → fullness 1–5
- When a user starts a Lock In session and rates a space, the app increments `space_counter` and adds the rating to `space_capacity` (weighted). Firestore is updated via `updateSpaceCapacityAndCounter`.
- **Community ratings** (1–5) are stored in LocalStorage and used for Discover scoring and SpaceCard display.

### AI Features (aiClient.js)

| Function | Purpose |
|----------|---------|
| `getAISpaceRecommendations` | Matches spaces to preferences; returns JSON array with space, library, reason |
| `getSessionCoachingSummary` | Post-session tip based on task, energy, duration |
| `getWeeklyStudyInsights` | Interpret weekly stats into actionable suggestions |
|

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 26 recognized source files, 186 KB.
- CSS (language) — detected in the code
- Firebase (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Anthropic (technology) — claimed on Devpost, not found in the code
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (30 of 30)

```
.gitignore
react-app/index.html
react-app/package.json
react-app/public/pdf.worker.min.mjs
react-app/scripts/seed-firestore.js
react-app/src/App.jsx
react-app/src/components/FooterNav.jsx
react-app/src/components/Logo.jsx
react-app/src/components/SpaceCard.jsx
react-app/src/firebase/firebase_utility.jsx
react-app/src/firebase/firebase.js
react-app/src/firebase/firebasetest.jsx
react-app/src/firebase/FirebaseUtilityTest.jsx
react-app/src/firebase/seed_columbia_libraries.js.js
react-app/src/hooks/useRecommender.js
react-app/src/main.jsx
react-app/src/pages/DiscoverPage.jsx
react-app/src/pages/HomePage.jsx
react-app/src/pages/LibraryPage.jsx
react-app/src/pages/LockPage.jsx
react-app/src/pages/StatsPage.jsx
react-app/src/services/aiClient.js
react-app/src/services/firebase.js
react-app/src/services/pdfParser.js
react-app/src/state/AppState.jsx
react-app/src/styles.css
react-app/src/theme.css
react-app/vercel.json
react-app/vite.config.js
README.md
```

### Dependencies

- react-app/package.json: @vitejs/plugin-react@^4.3.3, firebase@^12.9.0, firebase-admin@^13.6.1, firebase-tools@^13.0.0, pdfjs-dist@^5.4.624, react@^18.3.1, react-dom@^18.3.1, react-router-dom@^6.28.0, recharts@^3.7.0, vite@^6.0.0

### Recent commits (newest first)

- final touches for demo:
- Merge pull request #2 from zaryaabkhan/nnegro/bestBetBugFix
- Remove License section from README
- Update README with hackathon content and technical documentation
- Exam to-do: add date and countdown in list and study to-do section
- Noise: silent/busy/buzz, Find me a spot AI, 10s=10min demo, weekly 0/40hrs, syllabus exam subject+type
- changing sorting logic
- Merge pull request #1 from zaryaabkhan/nnegron/firebase_setup
- we are practically done
- StudyTransit: Columbia theme, Firebase, exam workflow, AI features, book favicon
- firebase
- firebase setup
- firebase inital setup
- Merge branch 'layout' of https://github.com/zaryaabkhan/studytransit into nnegron/firebase_setup
- fuck pt2
- fuck
- package.json
- StudyTransit updates: Claude AI, Lock In flow, home library stats, UI polish
- Initial commit: StudyTransit app layout

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

### react-app/package.json

```
{
  "name": "studytransit-react",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "seed:firestore": "node src/firebase/seed_columbia_libraries.js.js"
  },
  "dependencies": {
    "firebase": "^12.9.0",
    "firebase-admin": "^13.6.1",
    "pdfjs-dist": "^5.4.624",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.28.0",
    "recharts": "^3.7.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.3.3",
    "firebase-tools": "^13.0.0",
    "vite": "^6.0.0"
  }
}

```

### react-app/src/main.jsx

```javascript
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App.jsx";
import "./theme.css";
import "./styles.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);


```

### react-app/src/App.jsx

```javascript
import React from "react";
import { Routes, Route, Navigate } from "react-router-dom";
import { AppStateProvider } from "./state/AppState.jsx";
import { HomePage } from "./pages/HomePage.jsx";
import { LibraryPage } from "./pages/LibraryPage.jsx";
import { LockPage } from "./pages/LockPage.jsx";
import { DiscoverPage } from "./pages/DiscoverPage.jsx";
import { StatsPage } from "./pages/StatsPage.jsx";
import { FooterNav } from "./components/FooterNav.jsx";
import FirebaseUtilityTest from "./firebase/FirebaseUtilityTest.jsx";

function AppShell({ children }) {
  return (
    <div className="app-shell">
      {children}
      <FooterNav />
    </div>
  );
}

export default function App() {
  return (
    <AppStateProvider>
      <Routes>
        <Route
          path="/"
          element={
            <AppShell>
              <HomePage />
            </AppShell>
          }
        />
        <Route
          path="/libraries/:id"
          element={
            <AppShell>
              <LibraryPage />
            </AppShell>
          }
        />
        <Route
          path="/lock"
          element={
            <AppShell>
              <LockPage />
            </AppShell>
          }
        />
        <Route
          path="/discover"
          element={
            <AppShell>
              <DiscoverPage />
            </AppShell>
          }
        />
        <Route
          path="/stats"
          element={
            <AppShell>
              <StatsPage />
            </AppShell>
          }
        />
        <Route
          path="/firebase-test"
          element={
            <AppShell>
              <FirebaseUtilityTest />
            </AppShell>
          }
        />
        <Route path="*" element={<Navigate to="/" replace />} />
      </Routes>
    </AppStateProvider>
  );
}


```

### react-app/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
    <title>StudyTransit — Find your flow</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/svg+xml" href="/icon.svg" />
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>


```

### react-app/vite.config.js

```javascript
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");
  const apiKey = env.VITE_ANTHROPIC_API_KEY?.trim() || "";

  return {
    plugins: [react()],
    envDir: ".",
    server: {
      port: 5173,
      proxy: apiKey
        ? {
            "/api/anthropic": {
              target: "https://api.anthropic.com",
              changeOrigin: true,
              rewrite: (path) => path.replace(/^\/api\/anthropic/, ""),
              configure: (proxy) => {
                proxy.on("proxyReq", (proxyReq) => {
                  proxyReq.setHeader("x-api-key", apiKey);
                  proxyReq.setHeader("anthropic-version", "2023-06-01");
                  proxyReq.setHeader("content-type", "application/json");
                  proxyReq.setHeader("anthropic-dangerous-direct-browser-access", "true");
                });
              },
            },
          }
        : {},
    },
  };
});


```

### react-app/src/theme.css

```css
/* StudyTransit — Columbia University themed
   Columbia Primary Blue, Columbia Blue, Secondary Blues */

:root {
  --primary: #6cace4;
  --primary-light: #8fc2ec;
  --primary-dark: #4a90c2;
  --accent: #1d4f91;
  --accent-light: #dcebf8;
  --surface: #ffffff;
  --surface-warm: #f8fafc;
  --ink: #0f172a;
  --ink-muted: #475569;
  --primary-rgb: 108, 172, 228;
  --accent-rgb: 29, 79, 145;
  --occupancy-empty: #e2e8f0;
  --occupancy-low: #0d9488;
  --occupancy-mid: #d97706;
  --occupancy-high: #b91c1c;
}

```

### react-app/scripts/seed-firestore.js

```javascript
/**
 * Seed Firestore with Columbia libraries and spaces.
 * Run with: node scripts/seed-firestore.js
 * Requires Firebase Admin SDK or use the Firebase console to import.
 *
 * Collections:
 * - libraries: { id, name, location }
 * - spaces: { id, libraryId, name, capacity }
 */

const libraries = [
  { id: "lib-1", name: "Butler Library", location: "535 W 114th St" },
  { id: "lib-2", name: "Avery Architectural & Fine Arts", location: "Avery Hall" },
  { id: "lib-3", name: "Business & Economics (Uris)", location: "Uris Hall" },
  { id: "lib-4", name: "Lehman Social Sciences", location: "International Affairs" },
  { id: "lib-5", name: "Science & Engineering", location: "Northwest Corner Building" },
  { id: "lib-6", name: "Starr East Asian Library", location: "Kent Hall" },
  { id: "lib-7", name: "Music & Arts Library", location: "Dodge Hall" },
  { id: "lib-8", name: "Burke Library", location: "3041 Broadway (UTS)" },
  { id: "lib-9", name: "Social Work Library", location: "School of Social Work" },
  { id: "lib-10", name: "Barnard Milstein Center", location: "Barnard College" },
];

const spaces = [
  { id: "s1", libraryId: "lib-1", name: "Main Reading Room", capacity: 4 },
  { id: "s2", libraryId: "lib-1", name: "Floors 2–4 (24/7)", capacity: 4 },
  { id: "s3", libraryId: "lib-1", name: "Room 301 (Quiet)", capacity: 2 },
  { id: "s4", libraryId: "lib-1", name: "Rooms 502–504", capacity: 2 },
  { id: "s5", libraryId: "lib-1", name: "Rooms 601–607", capacity: 2 },
  { id: "s6", libraryId: "lib-1", name: "Stacks", capacity: 3 },
  { id: "s7", libraryId: "lib-1", name: "Rooms 202, 209 (Group)", capacity: 4 },
  { id: "s8", libraryId: "lib-1", name: "Rooms 403A–409A (Group)", capacity: 5 },
  { id: "s9", libraryId: "lib-2", name: "Main Reading Room", capacity: 3 },
  { id: "s10", libraryId: "lib-2", name: "Quiet Study Area", capacity: 2 },
  { id: "s11", libraryId: "lib-3", name: "Floors 1–2 (Group)", capacity: 4 },
  { id: "s12", libraryId: "lib-3", name: "3rd Floor (Quiet)", capacity: 3 },
  { id: "s13", libraryId: "lib-3", name: "Study Rooms", capacity: 5 },
  { id: "s14", libraryId: "lib-4", name: "Room 329A", capacity: 3 },
  { id: "s15", libraryId: "lib-4", name: "Main Floor", capacity: 4 },
  { id: "s16", libraryId: "lib-4", name: "Group Study", capacity: 5 },
  { id: "s17", libraryId: "lib-5", name: "400 Level", capacity: 4 },
  { id: "s18", libraryId: "lib-5", name: "Quiet Study", capacity: 3 },
  { id: "s19", libraryId: "lib-5", name: "Lab Space", capacity: 4 },
  { id: "s20", libraryId: "lib-6", name: "Main Reading Room", capacity: 3 },
  { id: "s21", libraryId: "lib-6", name: "Quiet Study", capacity: 2 },
  { id: "s22", libraryId: "lib-7", name: "Main Floor", capacity: 3 },
  { id: "s23", libraryId: "lib-7", name: "Listening Room", capacity: 2 },
  { id: "s24", libraryId: "lib-8", name: "Main Reading Room", capacity: 3 },
  { id: "s25", libraryId: "lib-8", name: "Quiet Study", capacity: 2 },
  { id: "s26", libraryId: "lib-9", name: "Main Floor", capacity: 4 },
  { id: "s27", libraryId: "lib-9", name: "Group Study", capacity: 5 },
  { id: "s28", libraryId: "lib-10", name: "Upper Floors", capacity: 4 },
  { id: "s29", libraryId: "lib-10", name: "Group Study", capacity: 5 },
];

console.log("Firestore seed data (paste into Firebase Console or use Admin SDK):\n");
console.log("Collection: libraries");
console.log(JSON.stringify(libraries, null, 2));
console.log("\nCollection: spaces");
console.log(JSON.stringify(spaces, null, 2));

```

### react-app/src/components/Logo.jsx

```javascript
import React from "react";

export function Logo({ size = 40, className = "" }) {
  return (
    <span className={className} role="img" aria-label="Book" style={{ fontSize: size, lineHeight: 1 }}>
      📖
    </span>
  );
}

```

### react-app/src/services/pdfParser.js

```javascript
import * as pdfjsLib from "pdfjs-dist";

if (typeof window !== "undefined") {
  pdfjsLib.GlobalWorkerOptions.workerSrc = window.location.origin + "/pdf.worker.min.mjs";
}

/**
 * Extract text from a PDF file.
 * @param {File} file - PDF file from input
 * @returns {Promise<string>} - Extracted text
 */
export async function extractTextFromPdf(file) {
  const arrayBuffer = await file.arrayBuffer();
  const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
  const numPages = pdf.numPages;
  const chunks = [];

  for (let i = 1; i <= numPages; i++) {
    const page = await pdf.getPage(i);
    const content = await page.getTextContent();
    const text = content.items.map((item) => item.str).join(" ");
    chunks.push(text);
  }

  return chunks.join("\n\n");
}

```

### react-app/src/firebase/firebase.js

```javascript
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getFirestore } from "firebase/firestore";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: "treehacksstudytransit.firebasestorage.app",
  messagingSenderId: "641874478315",
  appId: "1:641874478315:web:82b1e7174973c82074eaae",
  measurementId: "G-Y2SYJ235QX"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
```

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