# Project export: CanvasCal

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: CruzHacks 2026
- Tagline: Canvas Cal turns Canvas assignments and syllabi into a ready-to-use calendar and lets students manage it just by talking to an AI agent instead of manually setting up Google Calendar.
- Devpost: https://devpost.com/software/canvascal
- GitHub: https://github.com/Max-Ratcliff/CanvasCal
- Video: https://www.youtube.com/embed/8L1efF6JfiY?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — KeeperOfTheFire (18 commits), Sharanya Ramanujan (3 commits), Jiya-S (1 commits), Tarun Nagaraju (1 commits)

## Devpost submission (written by the team)

### Inspiration

Every student has Canvas. Every student also has calendar anxiety. Assignments pile up. Deadlines sneak up. Syllabi live as PDFs you open once and forget. Canvas technically has a calendar—but it’s cluttered, passive, and disconnected from how students actually manage their time. We kept asking the same question: Why doesn’t my calendar just know what’s due? Why can’t Google Calendar already understand my classes, my assignments, my exams, and the reality that I need time to walk across campus, time to study, and time to breathe? We, and basically every friend we talked to, were frustrated by how much mental effort it takes just to stay oriented. That lack of clarity hurts productivity, increases stress, and ultimately affects academic performance. So we built CanvasCal for the UCSC community: a tool that doesn’t just list deadlines, but actively helps you plan around them.

### What it does

📆 Smart Academic Calendar Pulls assignments, due dates, and announcements directly from Canvas Displays everything cleanly in one place One-click export to Google Calendar, so deadlines actually live where students plan their lives 🤖 AI Syllabus Parser Drag-and-drop any syllabus PDF Gemini-powered OCR extracts: Class meeting times Exams & midterms Office hours Important academic dates Class meeting times Exams & midterms Office hours Important academic dates Turns static documents into structured, usable data 💬 Academic Agent (Chatbot) Natural language interface for your calendar Ask things like: “When can I study for CSE 101?” “Add a study block before my midterm.” “Delete my 8am lab on Friday.” “When can I study for CSE 101?” “Add a study block before my midterm.” “Delete my 8am lab on Friday.” Reads your schedule, finds free time, and updates events intelligently 🗺️ Campus Travel Logic (Prototype) Calculates realistic buffer time between UCSC campus locations Prevents impossible back-to-back events across campus

### How we built it

🧩 Frontend Framework: Next.js (React) + TypeScript Styling: Tailwind CSS Icons: Lucide Deployment: Vercel Optimized for speed, accessibility, and zero friction 🧠 Backend Framework: FastAPI (Python 3.11+) AI Model: Google Gemini 2.0 Flash PDF Processing: PyMuPDF Integrations: Canvas API ☁️ Infrastructure Database: Supabase (PostgreSQL) Security: Row Level Security (RLS) Built to scale safely as usage grows

### Challenges we ran into

Syllabus Variability: There is no standard syllabus format. PDFs included scans, tables, multi-column layouts, and inconsistent date phrasing, which made reliable extraction non-trivial. Syllabus Variability: There is no standard syllabus format. PDFs included scans, tables, multi-column layouts, and inconsistent date phrasing, which made reliable extraction non-trivial. LLM Reliability: Model output could not be trusted directly. We had to constrain responses to fixed schemas and validate all extracted fields before creating calendar events. LLM Reliability: Model output could not be trusted directly. We had to constrain responses to fixed schemas and validate all extracted fields before creating calendar events. Event Consistency: Combining Canvas sync, syllabus events, and user edits caused duplication and overwrite issues early on. Ensuring idempotent writes and stable identifiers was required to keep the calendar coherent. Event Consistency: Combining Canvas sync, syllabus events, and user edits caused duplication and overwrite issues early on. Ensuring idempotent writes and stable identifiers was required to keep the calendar coherent. Vague Prompts: User prompts were often incomplete or vague. Resolving relative times and underspecified actions required additional parsing and fallback logic. Vague Prompts: User prompts were often incomplete or vague. Resolving relative times and underspecified actions required additional parsing and fallback logic. Inference Latency: PDF parsing and LLM calls added noticeable delay. We had to reduce prompt size, limit context, and avoid unnecessary calls to keep interactions responsive. Inference Latency: PDF parsing and LLM calls added noticeable delay. We had to reduce prompt size, limit context, and avoid unnecessary calls to keep interactions responsive. Travel Time Estimation: Campus navigation is context-dependent. A fully accurate model was out of scope, so we implemented a lightweight approximation that still prevented impossible schedules. Travel Time Estimation: Campus navigation is context-dependent. A fully accurate model was out of scope, so we implemented a lightweight approximation that still prevented impossible schedules. Access Control: Calendar data is sensitive. Supabase RLS policies had to be carefully scoped to prevent cross-user access. Access Control: Calendar data is sensitive. Supabase RLS policies had to be carefully scoped to prevent cross-user access. Time Constraints: Building a modular system while shipping a working product during a hackathon required constant trade-offs. Time Constraints: Building a modular system while shipping a working product during a hackathon required constant trade-offs.

### Accomplishments we're proud of

Extracting structured academic data from real-world, messy syllabi Building a usable AI agent instead of a demo chatbot Making Canvas data actually actionable Designing a system that feels helpful, not overwhelming Shipping a fully working end-to-end product during a hackathon 😤

### What we learned

We've gained deeper understanding of how UI should be dealt with and how our targeted audience should affect our design. Academic data is highly unstructured in practice. Syllabi vary wildly in format so we learned how to extract the same data from a variety of them. Integrating AI into productivity tools requires strong guardrails Real-time calendar systems needs careful building. Users expect instant, low-latency interactions from AI agents, which significantly influences architectural decisions. Privacy and data isolation are foundational when dealing with academic records, making row-level security and scoped access essential. Effective time management systems must reason about context (travel time, workload density, availability), not just store events.

### What's next

Voice Agent Integration Hands-free scheduling and calendar edits Voice Agent Integration Hands-free scheduling and calendar edits Two-Way Google Calendar Sync Updates flow both directions in real time Two-Way Google Calendar Sync Updates flow both directions in real time Smart Study Recommendations Auto-suggest study blocks based on workload and deadlines Smart Study Recommendations Auto-suggest study blocks based on workload and deadlines Deeper Canvas Support Quizzes, discussions, grading windows, and announcements Deeper Canvas Support Quizzes, discussions, grading windows, and announcements Personalized Time Intelligence Learns from user behavior to improve scheduling over time Personalized Time Intelligence Learns from user behavior to improve scheduling over time

## README (from the GitHub repository)

# CanvasCal 📅
### CruzHacks 2026

**CanvasCal** is an AI-native productivity ecosystem designed to bridge the gap between academic requirements and daily action. By integrating directly with Canvas LMS and utilizing Gemini-powered OCR for syllabus parsing, it automatically transforms static course documents into an intelligent, active schedule.

![Tech Stack](https://img.shields.io/badge/Tech-FastAPI%20%7C%20React%20%7C%20Gemini%202.0%20%7C%20Supabase-blue)

## 🚀 Features

-   **🤖 AI Syllabus Parser:** Drag & drop a PDF syllabus, and our Gemini 2.0 parser extracts dates, exams, and assignments with high precision.
-   **💬 Academic Agent:** A natural language chatbot that can check your schedule, add study blocks, and find free time ("When can I study for CSE 101?").
-   **🔗 Canvas Integration:** Live sync with Canvas API to pull real-time assignment due dates and announcements.
-   **🗺️ Campus Travel Logic:** (Prototype) Intelligent buffers that calculate travel time between UCSC campus locations.
-   **☁️ Supabase Backend:** Secure, scalable event storage with Row Level Security (RLS).

## 🛠️ Architecture

### Frontend (`/frontend`)
-   **Framework:** Next.js (React) with TypeScript
-   **Styling:** Tailwind CSS + Lucide Icons
-   **Deployment:** Vercel / Local Vite (for dev)

### Backend (`/backend`)
-   **Framework:** FastAPI (Python 3.11+)
-   **AI Model:** Google Gemini 2.0 Flash
-   **Database:** Supabase (PostgreSQL)
-   **PDF Processing:** PyMuPDF

## 📦 Setup Guide

### Prerequisites
-   Node.js 18+ & npm/pnpm
-   Python 3.11+
-   Supabase Account
-   Google AI Studio API Key

### 1. Backend Setup
```bash
cd backend
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -r requirements.txt
```

Create a `.env` file in `/backend`:
```ini
PROJECT_NAME="CanvasCal"
BACKEND_CORS_ORIGINS=["http://localhost:5173"]

# Integrations
GEMINI_API_KEY="your_gemini_key"
CANVAS_API_URL="https://canvas.instructure.com" # or your school's instance
CANVAS_ACCESS_TOKEN="your_canvas_token"

# Database
SUPABASE_URL="your_supabase_url"
SUPABASE_KEY="your_supabase_anon_key"
```

### 2. Frontend Setup
```bash
cd frontend
npm install
```

Create a `.env.local` in `/frontend`:
```ini
NEXT_PUBLIC_API_URL="http://localhost:8000"
```

### 3. Database Schema
Run the SQL found in `backend/schema.sql` in your Supabase SQL Editor to create the necessary tables.

### 4. Running the App
We have a unified start script for convenience:
```bash
./start.sh
```
This will launch:
-   Backend: http://localhost:8000 (Docs: /docs)
-   Frontend: http://localhost:5173

## 🧪 Testing

Run the backend test suite:
```bash
cd backend
pytest tests/
```

## 👥 Contributors
-   Max Ratcliff
-   Sharanya Ramanujan
-   Jiya Sawarkar
-   Tarun Nagaraju

---
*Built for CruzHacks 2026*


## Detected evidence (automated analysis)

Indexed codebase: 124 recognized source files, 421 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 137)

```
.gitignore
AUTH_PLAN.md
backend_desc.md
backend/.env.example
backend/app/__init__.py
backend/app/api/__init__.py
backend/app/api/agent.py
backend/app/api/auth.py
backend/app/api/calendar.py
backend/app/api/canvas.py
backend/app/api/syllabus.py
backend/app/core/config.py
backend/app/core/security.py
backend/app/db.py
backend/app/main.py
backend/app/schemas/__init__.py
backend/app/schemas/event.py
backend/app/schemas/response.py
backend/app/services/__init__.py
backend/app/services/agent.py
backend/app/services/campus_logic.py
backend/app/services/crypto.py
backend/app/services/google_calendar.py
backend/app/services/parser.py
backend/app/services/scheduler.py
backend/app/services/storage_supabase.py
backend/app/services/storage.py
backend/backend/data/events.json
backend/requirements.txt
backend/schema.sql
backend/scripts/check_integrations.py
backend/scripts/manual_test_canvas.py
backend/scripts/test_gemini_connection.py
backend/scripts/test_supabase_simple.py
backend/tests/test_agent_tools.py
backend/tests/test_calendar_api_auth.py
backend/tests/test_campus_logic.py
backend/tests/test_canvas.py
backend/tests/test_google_cal_isolation.py
backend/tests/test_google_cal_unit.py
backend/tests/test_google_sync_integration.py
backend/tests/test_syllabus.py
CALENDAR_INTEGRATION_PLAN.md
frontend_v1_backup/index.html
frontend_v1_backup/package.json
frontend_v1_backup/README.md
frontend_v1_backup/src/App.tsx
frontend_v1_backup/src/Attributions.md
frontend_v1_backup/src/components/ai-agent.tsx
frontend_v1_backup/src/components/announcements.tsx
frontend_v1_backup/src/components/assignment-checklist.tsx
frontend_v1_backup/src/components/calendar.tsx
frontend_v1_backup/src/components/figma/ImageWithFallback.tsx
frontend_v1_backup/src/components/ui/accordion.tsx
frontend_v1_backup/src/components/ui/alert-dialog.tsx
frontend_v1_backup/src/components/ui/alert.tsx
frontend_v1_backup/src/components/ui/aspect-ratio.tsx
frontend_v1_backup/src/components/ui/avatar.tsx
frontend_v1_backup/src/components/ui/badge.tsx
frontend_v1_backup/src/components/ui/breadcrumb.tsx
frontend_v1_backup/src/components/ui/button.tsx
frontend_v1_backup/src/components/ui/calendar.tsx
frontend_v1_backup/src/components/ui/card.tsx
frontend_v1_backup/src/components/ui/carousel.tsx
frontend_v1_backup/src/components/ui/chart.tsx
frontend_v1_backup/src/components/ui/checkbox.tsx
frontend_v1_backup/src/components/ui/collapsible.tsx
frontend_v1_backup/src/components/ui/command.tsx
frontend_v1_backup/src/components/ui/const API_BASE_URL = import.meta.env.VIT
frontend_v1_backup/src/components/ui/context-menu.tsx
frontend_v1_backup/src/components/ui/dialog.tsx
frontend_v1_backup/src/components/ui/drawer.tsx
frontend_v1_backup/src/components/ui/dropdown-menu.tsx
frontend_v1_backup/src/components/ui/form.tsx
frontend_v1_backup/src/components/ui/hover-card.tsx
frontend_v1_backup/src/components/ui/input-otp.tsx
frontend_v1_backup/src/components/ui/input.tsx
frontend_v1_backup/src/components/ui/label.tsx
frontend_v1_backup/src/components/ui/menubar.tsx
frontend_v1_backup/src/components/ui/navigation-menu.tsx
frontend_v1_backup/src/components/ui/pagination.tsx
frontend_v1_backup/src/components/ui/popover.tsx
frontend_v1_backup/src/components/ui/progress.tsx
frontend_v1_backup/src/components/ui/radio-group.tsx
frontend_v1_backup/src/components/ui/resizable.tsx
frontend_v1_backup/src/components/ui/scroll-area.tsx
frontend_v1_backup/src/components/ui/select.tsx
frontend_v1_backup/src/components/ui/separator.tsx
frontend_v1_backup/src/components/ui/sheet.tsx
frontend_v1_backup/src/components/ui/sidebar.tsx
frontend_v1_backup/src/components/ui/skeleton.tsx
frontend_v1_backup/src/components/ui/slider.tsx
frontend_v1_backup/src/components/ui/sonner.tsx
frontend_v1_backup/src/components/ui/switch.tsx
frontend_v1_backup/src/components/ui/table.tsx
frontend_v1_backup/src/components/ui/tabs.tsx
frontend_v1_backup/src/components/ui/textarea.tsx
frontend_v1_backup/src/components/ui/toggle-group.tsx
frontend_v1_backup/src/components/ui/toggle.tsx
frontend_v1_backup/src/components/ui/tooltip.tsx
frontend_v1_backup/src/components/ui/use-mobile.ts
frontend_v1_backup/src/components/ui/utils.ts
frontend_v1_backup/src/guidelines/Guidelines.md
frontend_v1_backup/src/index.css
frontend_v1_backup/src/main.tsx
frontend_v1_backup/src/services/api.ts
frontend_v1_backup/src/styles/globals.css
frontend_v1_backup/vite.config.ts
frontend/.gitignore
frontend/app/globals.css
frontend/app/layout.tsx
frontend/app/page.tsx
frontend/app/providers.tsx
frontend/components.json
frontend/components/ai-assistant.tsx
frontend/components/banana-slug-background.tsx
frontend/components/calendar.tsx
frontend/components/syllabus-viewer.tsx
frontend/components/theme-provider.tsx
frontend/components/upcoming-card.tsx
[17 more files omitted for size]
```

### Dependencies

- backend/requirements.txt: canvasapi, fastapi, google-api-python-client, google-auth-oauthlib, google-genai, httpxcryptography, pydantic, pydantic-settings, PyMuPDF, pytest, python-dotenv, python-multipart, supabase, uvicorn
- frontend_v1_backup/package.json: @radix-ui/react-accordion@^1.2.3, @radix-ui/react-alert-dialog@^1.1.6, @radix-ui/react-aspect-ratio@^1.1.2, @radix-ui/react-avatar@^1.1.3, @radix-ui/react-checkbox@^1.1.4, @radix-ui/react-collapsible@^1.1.3, @radix-ui/react-context-menu@^2.2.6, @radix-ui/react-dialog@^1.1.6, @radix-ui/react-dropdown-menu@^2.1.6, @radix-ui/react-hover-card@^1.1.6, @radix-ui/react-label@^2.1.2, @radix-ui/react-menubar@^1.1.6, @radix-ui/react-navigation-menu@^1.2.5, @radix-ui/react-popover@^1.1.6, @radix-ui/react-progress@^1.1.2, @radix-ui/react-radio-group@^1.2.3, @radix-ui/react-scroll-area@^1.2.3, @radix-ui/react-select@^2.1.6, @radix-ui/react-separator@^1.1.2, @radix-ui/react-slider@^1.2.3, @radix-ui/react-slot@^1.1.2, @radix-ui/react-switch@^1.1.3, @radix-ui/react-tabs@^1.1.3, @radix-ui/react-toggle@^1.1.2, @radix-ui/react-toggle-group@^1.1.2, @radix-ui/react-tooltip@^1.1.8, @types/node@^20.10.0, @vitejs/plugin-react-swc@^3.10.2, class-variance-authority@^0.7.1, clsx@*, cmdk@^1.1.1, embla-carousel-react@^8.6.0, input-otp@^1.4.2, lucide-react@^0.487.0, next-themes@^0.4.6, react@^18.3.1, react-day-picker@^8.10.1, react-dom@^18.3.1, react-hook-form@^7.55.0, react-resizable-panels@^2.1.7, recharts@^2.15.2, sonner@^2.0.3, tailwind-merge@*, vaul@^1.1.2, vite@6.3.5
- frontend/package.json: @hookform/resolvers@^3.10.0, @radix-ui/react-accordion@1.2.2, @radix-ui/react-alert-dialog@1.1.4, @radix-ui/react-aspect-ratio@1.1.1, @radix-ui/react-avatar@1.1.2, @radix-ui/react-checkbox@1.1.3, @radix-ui/react-collapsible@1.1.2, @radix-ui/react-context-menu@2.2.4, @radix-ui/react-dialog@1.1.4, @radix-ui/react-dropdown-menu@2.1.4, @radix-ui/react-hover-card@1.1.4, @radix-ui/react-label@2.1.1, @radix-ui/react-menubar@1.1.4, @radix-ui/react-navigation-menu@1.2.3, @radix-ui/react-popover@1.1.4, @radix-ui/react-progress@1.1.1, @radix-ui/react-radio-group@1.2.2, @radix-ui/react-scroll-area@1.2.2, @radix-ui/react-select@2.1.4, @radix-ui/react-separator@1.1.1, @radix-ui/react-slider@1.2.2, @radix-ui/react-slot@1.1.1, @radix-ui/react-switch@1.1.2, @radix-ui/react-tabs@1.1.2, @radix-ui/react-toast@1.2.4, @radix-ui/react-toggle@1.1.1, @radix-ui/react-toggle-group@1.1.1, @radix-ui/react-tooltip@1.1.6, @react-oauth/google@^0.13.4, @supabase/supabase-js@^2.90.1, @tailwindcss/postcss@^4.1.9, @types/node@^22, @types/react@^19, @types/react-dom@^19, @vercel/analytics@1.3.1, autoprefixer@^10.4.20, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@1.0.4, date-fns@4.1.0, embla-carousel-react@8.5.1, input-otp@1.4.1, lucide-react@^0.454.0, next@16.0.10, next-themes@^0.4.6, postcss@^8.5, react@19.2.0, react-day-picker@9.8.0, react-dom@19.2.0, react-hook-form@^7.60.0, react-resizable-panels@^2.1.7, recharts@2.15.4, sonner@^1.7.4, tailwind-merge@^3.3.1, tailwindcss@^4.1.9, tailwindcss-animate@^1.0.7, tw-animate-css@1.3.3, typescript@^5, vaul@^1.1.2, zod@3.25.76

### Recent commits (newest first)

- README.md
- pdf viewer working
- syllabus hub
- better canvas token handling
- pushing to custom calendar
- much smarter agent
- agent add and delete working
- significant chatbot improvements
- chat bot working
- prettier + loading anims
- feat: Implement Auto-Sync, Magic Link Auth, and Secure Google Cal Isolation
- syllabus parsing
- fix calendar view
- canvas hooked up to front end
- canvas integration
- env.ex
- git ignore and some canvas logic
- Merge remote-tracking branch 'origin/main' into main
- feat: setup supabase client and dependencies
- chore: Clean up git repository and add .gitignore

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

### SESSION_SUMMARY.md

```markdown
# Session Summary: Canvas Integration & System Resilience

**Date:** January 18, 2026 (Mid-Morning Update)

## 🏆 Key Achievements

1.  **Robust Canvas Integration:**
    -   **Manual Pivot:** Implemented a guided "Manual Token" flow with clear instructions and a direct link to Canvas settings, bypassing the need for restricted Developer Keys.
    -   **Token Security:** Implemented AES-256 encryption/decryption for Canvas tokens, ensuring they are stored safely in the database.
    -   **Typo Protection:** Added backend logic to automatically correct common URL typos (e.g., `instructure.edu` -> `instructure.com`).
    -   **Sync Resilience:** Fixed Google Calendar "Empty Time Range" errors by ensuring assignments have a 30-minute duration.
    -   **Syllabus PDF Fix:** Improved background syllabus parsing to correctly follow redirects (AWS/S3) when downloading from Canvas.

2.  **AI Agent Memory & UX:**
    -   **Direct ID Access:** The Agent now receives database IDs in its "working memory" (context injection). It can now delete or update events it sees without performing a search first.
    -   **Dynamic Chat UI:** Refined the chat window to be responsive (`calc(100vh - 140px)`) and implemented a slim, custom scrollbar for better focus.

3.  **UI Data Architecture:**
    -   **Single Source of Truth:** Unified the "Upcoming" (To-Do) list to pull directly from the local database instead of the Canvas API.
    -   **Live Updates:** The entire UI (Calendar + To-Do) now listens for a `calendar-updated` event, ensuring real-time consistency whenever the Agent or Canvas Sync acts.

## 🔜 Next Steps (Planned)

1.  **Syllabus Intelligence Hub:** Finalize the dashboard for browsing parsed course insights (grading, policies, office hours).
2.  **External Calendar Toggles:** Implement the ability to fetch and display events from *other* Google Calendars (Primary, Work, etc.) within the Agent's context.
3.  **Refine Editing:** Continue tuning the `update_calendar_event` tool for complex rescheduling requests.

---

## 📜 Previous Achievements
-   **Auth:** Supabase Magic Link + Google Offline Access.
-   **Isolation:** Automated "CanvasCal" sub-calendar creation.
-   **Context:** Dynamic timezone detection (Browser -> Agent).

```

### MASTER_CALENDAR_SYSTEM_PLAN.md

```markdown
# Master Blueprint: CanvasCal Titanium-Grade Calendar Engine

## 1. Objective
Build a resilient, high-performance, and security-first synchronization engine that bridges Canvas LMS, AI-driven scheduling, and Google Calendar.

---

## 2. Security & Authentication [COMPLETED]
-   **Auth:** Supabase Magic Link (App) + Google OAuth2 (Calendar Sync).
-   **Encryption:** AES-256 for all third-party tokens (Google Refresh & Canvas Access).
-   **RLS:** Row Level Security enforced on all tables; Service Role used for background sync.

---

## 3. Synchronization Engine [COMPLETED]
-   **Canvas:** Manual token integration with auto-correction for school URLs.
-   **Assignments:** Auto-sync with deterministic UUIDs to prevent duplicates.
-   **Google:** Fully isolated "CanvasCal" secondary calendar support.
-   **Stability:** Handles empty time ranges and S3 redirects for PDFs.

---

## 4. AI Agent & Intelligence [IN PROGRESS]
-   **Memory:** Full conversational history + 7-day "Context Injection" with direct Database IDs.
-   **Timezone:** Automated detection via browser (ISO 8601 with local offsets).
-   **Tools:**
    -   [x] `add_calendar_event`
    -   [x] `get_calendar_events` (Keyword search)
    -   [x] `delete_calendar_event` (Global sync)
    -   [x] `update_calendar_event` (Edit capability)
-   **UI:** Responsive chat window with custom scrolling.

---

## 5. Syllabus Intelligence Hub [IN PROGRESS]
-   **Backend:** Database updated to store `course_insights` (JSONB).
-   **Frontend:** `SyllabusViewer` component designed for course-specific insights.
-   **AI:** Extraction improved to capture Grading, Policies, and Office Hours.

---

## 6. Implementation Roadmap

### Phase 1: Core Foundation [COMPLETED]
- [x] Auth & Security
- [x] Canvas Assignment Sync
- [x] Google Calendar Creation

### Phase 2: Agent Refinement [COMPLETED]
- [x] Editing & Deletion logic
- [x] Context Injection (Event Memory)
- [x] UI Polish (Scrolling/Focus)

### Phase 3: Syllabus "Brain" [NEXT]
- [x] **Backend:** Schema and API for course insights.
- [ ] **UI:** Finalize the Modal/Overlay integration in `page.tsx`.

### Phase 4: External Context [PLANNED]
- [ ] **Visibility:** Fetch external Google events (toggable).
- [ ] **Deployment:** Final Render/Vercel setup.

```

### backend/requirements.txt

```
fastapi
uvicorn
python-dotenv
python-multipart
canvasapi
google-api-python-client
google-auth-oauthlib
google-genai
PyMuPDF
pydantic
pydantic-settings
supabase
pytest
httpxcryptography

```

### frontend_v1_backup/package.json

```

  {
      "name": "School Calendar Export Page",
      "version": "0.1.0",
      "private": true,
      "dependencies": {
          "@radix-ui/react-accordion": "^1.2.3",
          "@radix-ui/react-alert-dialog": "^1.1.6",
          "@radix-ui/react-aspect-ratio": "^1.1.2",
          "@radix-ui/react-avatar": "^1.1.3",
          "@radix-ui/react-checkbox": "^1.1.4",
          "@radix-ui/react-collapsible": "^1.1.3",
          "@radix-ui/react-context-menu": "^2.2.6",
          "@radix-ui/react-dialog": "^1.1.6",
          "@radix-ui/react-dropdown-menu": "^2.1.6",
          "@radix-ui/react-hover-card": "^1.1.6",
          "@radix-ui/react-label": "^2.1.2",
          "@radix-ui/react-menubar": "^1.1.6",
          "@radix-ui/react-navigation-menu": "^1.2.5",
          "@radix-ui/react-popover": "^1.1.6",
          "@radix-ui/react-progress": "^1.1.2",
          "@radix-ui/react-radio-group": "^1.2.3",
          "@radix-ui/react-scroll-area": "^1.2.3",
          "@radix-ui/react-select": "^2.1.6",
          "@radix-ui/react-separator": "^1.1.2",
          "@radix-ui/react-slider": "^1.2.3",
          "@radix-ui/react-slot": "^1.1.2",
          "@radix-ui/react-switch": "^1.1.3",
          "@radix-ui/react-tabs": "^1.1.3",
          "@radix-ui/react-toggle": "^1.1.2",
          "@radix-ui/react-toggle-group": "^1.1.2",
          "@radix-ui/react-tooltip": "^1.1.8",
          "class-variance-authority": "^0.7.1",
          "clsx": "*",
          "cmdk": "^1.1.1",
          "embla-carousel-react": "^8.6.0",
          "input-otp": "^1.4.2",
          "lucide-react": "^0.487.0",
          "next-themes": "^0.4.6",
          "react": "^18.3.1",
          "react-day-picker": "^8.10.1",
          "react-dom": "^18.3.1",
          "react-hook-form": "^7.55.0",
          "react-resizable-panels": "^2.1.7",
          "recharts": "^2.15.2",
          "sonner": "^2.0.3",
          "tailwind-merge": "*",
          "vaul": "^1.1.2"
      },
      "devDependencies": {
          "@types/node": "^20.10.0",
          "@vitejs/plugin-react-swc": "^3.10.2",
          "vite": "6.3.5"
      },
      "scripts": {
          "dev": "vite",
          "build": "vite build"
      }
  }
```

### frontend/package.json

```
{
  "name": "my-v0-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "next build",
    "dev": "next dev -p 5173",
    "lint": "eslint .",
    "start": "next start"
  },
  "dependencies": {
    "@hookform/resolvers": "^3.10.0",
    "@radix-ui/react-accordion": "1.2.2",
    "@radix-ui/react-alert-dialog": "1.1.4",
    "@radix-ui/react-aspect-ratio": "1.1.1",
    "@radix-ui/react-avatar": "1.1.2",
    "@radix-ui/react-checkbox": "1.1.3",
    "@radix-ui/react-collapsible": "1.1.2",
    "@radix-ui/react-context-menu": "2.2.4",
    "@radix-ui/react-dialog": "1.1.4",
    "@radix-ui/react-dropdown-menu": "2.1.4",
    "@radix-ui/react-hover-card": "1.1.4",
    "@radix-ui/react-label": "2.1.1",
    "@radix-ui/react-menubar": "1.1.4",
    "@radix-ui/react-navigation-menu": "1.2.3",
    "@radix-ui/react-popover": "1.1.4",
    "@radix-ui/react-progress": "1.1.1",
    "@radix-ui/react-radio-group": "1.2.2",
    "@radix-ui/react-scroll-area": "1.2.2",
    "@radix-ui/react-select": "2.1.4",
    "@radix-ui/react-separator": "1.1.1",
    "@radix-ui/react-slider": "1.2.2",
    "@radix-ui/react-slot": "1.1.1",
    "@radix-ui/react-switch": "1.1.2",
    "@radix-ui/react-tabs": "1.1.2",
    "@radix-ui/react-toast": "1.2.4",
    "@radix-ui/react-toggle": "1.1.1",
    "@radix-ui/react-toggle-group": "1.1.1",
    "@radix-ui/react-tooltip": "1.1.6",
    "@react-oauth/google": "^0.13.4",
    "@supabase/supabase-js": "^2.90.1",
    "@vercel/analytics": "1.3.1",
    "autoprefixer": "^10.4.20",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "1.0.4",
    "date-fns": "4.1.0",
    "embla-carousel-react": "8.5.1",
    "input-otp": "1.4.1",
    "lucide-react": "^0.454.0",
    "next": "16.0.10",
    "next-themes": "^0.4.6",
    "react": "19.2.0",
    "react-day-picker": "9.8.0",
    "react-dom": "19.2.0",
    "react-hook-form": "^7.60.0",
    "react-resizable-panels": "^2.1.7",
    "recharts": "2.15.4",
    "sonner": "^1.7.4",
    "tailwind-merge": "^3.3.1",
    "tailwindcss-animate": "^1.0.7",
    "vaul": "^1.1.2",
    "zod": "3.25.76"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4.1.9",
    "@types/node": "^22",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "postcss": "^8.5",
    "tailwindcss": "^4.1.9",
    "tw-animate-css": "1.3.3",
    "typescript": "^5"
  }
}

```

### frontend_v1_backup/src/main.tsx

```typescript

  import { createRoot } from "react-dom/client";
  import App from "./App.tsx";
  import "./index.css";

  createRoot(document.getElementById("root")!).render(<App />);
  
```

### frontend/app/layout.tsx

```typescript
import React from "react"
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import { Analytics } from '@vercel/analytics/next'
import { Toaster } from "sonner"
import { Providers } from "./providers"
import './globals.css'

const _geist = Geist({ subsets: ["latin"] });
const _geistMono = Geist_Mono({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: 'CanvasCal - School Calendar Hub',
  description: 'Manage your academic schedule and export events to Google Calendar',
  generator: 'v0.app',
  icons: {
    icon: [
      {
        url: '/icon-light-32x32.png',
        media: '(prefers-color-scheme: light)',
      },
      {
        url: '/icon-dark-32x32.png',
        media: '(prefers-color-scheme: dark)',
      },
      {
        url: '/icon.svg',
        type: 'image/svg+xml',
      },
    ],
    apple: '/apple-icon.png',
  },
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body className={`font-sans antialiased`}>
        <Providers>
          {children}
          <Toaster position="top-center" />
        </Providers>
        <Analytics />
      </body>
    </html>
  )
}

```

### backend/app/main.py

```python
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from app.api import auth, syllabus, canvas, calendar, agent
from app.core.config import settings
import logging
import time

# Configure Logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger("CanvasCal")

app = FastAPI(title=settings.PROJECT_NAME, description="AI-native productivity ecosystem backend")

# Add CORS middleware to allow frontend communication
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.BACKEND_CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Request Logging Middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    logger.info(f"{request.method} {request.url.path} - {response.status_code} - {process_time:.4f}s")
    return response

app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
app.include_router(syllabus.router, prefix="/syllabus", tags=["Syllabus Processing"])
app.include_router(canvas.router, prefix="/canvas", tags=["Canvas Integration"])
app.include_router(calendar.router, prefix="/calendar", tags=["Google Calendar"])
app.include_router(agent.router, prefix="/agent", tags=["AI Agent"])

@app.get("/")
async def root():
    logger.info("Root endpoint accessed")
    return {"message": "Welcome to Canvas Cal API"}
```

### frontend_v1_backup/src/App.tsx

```typescript
import { useState } from 'react';
import { Calendar } from './components/calendar';
import { AIAgent } from './components/ai-agent';
import { AssignmentChecklist } from './components/assignment-checklist';
import { Announcements } from './components/announcements';
import { api } from './services/api';
import { Upload } from 'lucide-react';

export default function App() {
  const [uploading, setUploading] = useState(false);

  const handleSyllabusUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    setUploading(true);
    try {
      const response = await api.uploadSyllabus(file);
      if (response.success) {
        alert('Syllabus processed successfully! Events added to calendar.');
        // Trigger calendar refresh
        window.location.reload();
      }
    } catch (error) {
      console.error('Upload failed:', error);
      alert('Failed to process syllabus. Please try again.');
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="min-h-screen p-6" style={{ backgroundColor: '#ffc971' }}>
      <div className="max-w-7xl mx-auto">
        <header className="mb-6">
          <div className="flex items-center justify-between">
            <div>
              <h1 style={{ color: '#185177' }}>School Calendar Hub</h1>
              <p className="mt-1" style={{ color: '#c95603' }}>
                Manage your academic schedule and export events to Google Calendar
              </p>
            </div>
            
            <label className="flex items-center gap-2 px-4 py-2 text-white rounded-lg cursor-pointer transition-colors"
              style={{ backgroundColor: '#185177' }}
              onMouseEnter={(e) => e.currentTarget.style.backgroundColor = '#e2711d'}
              onMouseLeave={(e) => e.currentTarget.style.backgroundColor = '#185177'}>
              <Upload className="w-4 h-4" />
              {uploading ? 'Processing...' : 'Upload Syllabus'}
              <input
                type="file"
                accept=".pdf"
                onChange={handleSyllabusUpload}
                disabled={uploading}
                className="hidden"
              />
            </label>
          </div>
        </header>
        
        <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
          {/* Left Column - Calendar and Checklists */}
          <div className="lg:col-span-2 space-y-6">
            <Calendar />
            <Announcements />
            <AssignmentChecklist />
          </div>
          
          {/* Right Column - AI Agent */}
          <div className="lg:col-span-1">
            <AIAgent />
          </div>
        </div>
      </div>
    </div>
  );
}
```

### frontend/app/page.tsx

```typescript
"use client"

import { useRef, useState, useEffect } from "react"
import { 
  Upload, 
  Loader2, 
  Calendar as CalendarIcon, 
  LogOut, 
  LogIn, 
  CheckCircle2, 
  LayoutDashboard, 
  Plus, 
  BookOpen 
} from "lucide-react"
import { Calendar } from "@/components/calendar"
import { UpcomingCard } from "@/components/upcoming-card"
import { AIAssistant } from "@/components/ai-assistant"
import { SyllabusViewer } from "@/components/syllabus-viewer"
import { BananaSlugBackground } from "@/components/banana-slug-background"
import { ThemeProvider } from "@/components/theme-provider"
import { useAuth } from "./providers"
import { api } from "@/lib/api"
import { toast } from "sonner"

export default function Home() {
  const fileInputRef = useRef<HTMLInputElement>(null)
  const [isSyncingCanvas, setIsSyncingCanvas] = useState(false)
  const [isUploading, setIsUploading] = useState(false)
  const [isConnectingGoogle, setIsConnectingGoogle] = useState(false)
  const [showSyllabusViewer, setShowSyllabusViewer] = useState(false)
  const [showCanvasConnect, setShowCanvasConnect] = useState(false)
  const [canvasUrl, setCanvasUrl] = useState("https://canvas.instructure.com")
  const [canvasToken, setCanvasToken] = useState("")
  const [integrations, setIntegrations] = useState({ google_calendar: false, canvas: false })
  const [cachedCourses, setCachedCourses] = useState<any[]>([])
  const { session, signIn, signOut, token } = useAuth()

  const loginWithGoogle = () => {
    setIsConnectingGoogle(true)
    api.getGoogleAuthUrl(token || "")
      .then(res => {
        if (res.success && res.data?.url) {
          window.location.href = res.data.url
        }
      })
      .finally(() => setIsConnectingGoogle(false))
  }

  useEffect(() => {
    if (token) {
      api.getIntegrationsStatus(token)
        .then(res => {
          if (res.success) {
            setIntegrations(res.data)
            // If canvas is connected, pre-fetch courses
            if (res.data.canvas) {
              api.getCanvasCourses(token).then(courseRes => {
                if (courseRes.success) setCachedCourses(courseRes.data)
              })
            }
          }
        })
        .catch(console.error)
    }
  }, [token])

  const handleCanvasConnect = async (e: React.FormEvent) => {
    e.preventDefault()
    if (!token || !canvasUrl || !canvasToken) return
    setIsSyncingCanvas(true)
    try {
      const res = await api.connectCanvas(canvasUrl, canvasToken, token)
      if (res.success) {
        toast.success("Canvas connected!")
        setIntegrations(prev => ({ ...prev, canvas: true }))
        setShowCanvasConnect(false)
      } else {
        toast.error(res.message)
      }
    } catch (error) {
      toast.error("Failed to connect Canvas")
    } finally {
      setIsSyncingCanvas(false)
    }
  }

  const handleCanvasSync = async () => {
      if (!token) return
      if (!integrations.canvas) {
        setShowCanvasConnect(true)
        return
      }
      setIsSyncingCanvas(true)
      try {
          const response = await api.syncCanvas(token)
          if (response.success) {
              toast.success("Canvas sync started!")
          } else {
              toast.error(response.message || "Canvas authentication failed.")
              // If it failed, maybe the token is wrong. Let them re-connect.
              setShowCanvasConnect(true)
          }
      } catch (error) {
          toast.error("Failed to sync Canvas")
          setShowCanvasConnect(true)
      } finally {
          setIsSyncingCanvas(false)
      }
  }

  const [email, setEmail] = useState("")
  const [isSigningIn, setIsSigningIn] = useState(false)

  const handleSignIn = async (e: React.FormEvent) => {
    e.preventDefault()
    if (!email) return
    
    setIsSigningIn(true)
    try {
      await signIn(email)
      toast.success("Check your email for the login link!")
    } catch (error: any) {
      toast.error(error.message || "Failed to send login link")
    } finally {
      setIsSigningIn(false)
    }
  }

  return (
    <div className="min-h-screen bg-[#FFECD1] relative">
      <BananaSlugBackground />

      {/* Canvas Connection Overlay */}
      {showCanvasConnect && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm p-4">
          <div className="bg-white rounded-3xl shadow-xl w-full max-w-md p-8 animate-in zoom-in-95 duration-200">
            <h2 className="text-2xl font-bold text-[#2d4a5e] mb-2">Connect Canvas</h2>
            <p className="text-sm text-[#6b7c8a] mb-6">Follow these steps to link your school account:</p>
            
            <form onSubmit={handleCanvasConnect} className="space-y-6">
              <div>
                <label className="block text-sm font-semibold text-[#2d4a5e] mb-2">Canvas School URL</label>
                <input 
                  type="text" 
                  value={canvasUrl}
                  onChange={(e) => setCanvasUrl(e.target.value)}
                  placeholder="https://canvas.ucsc.edu"
                  className="w-full px-4 py-3 rounded-xl border border-[#2d4a5e]/20 text-sm focus:outline-none focus:border-[#2d4a5e]"
                  required
                />
              </div>

              <div className="bg-[#f4c542]/10 p-4 rounded-2xl border border-[#f4c542]/20">
                <p className="text-xs text-[#2d4a5e] leading-relaxed">
                  1. Click the button below to open your Canvas settings.<br/>
                  2. Scroll to <strong>"Approved Integrations"</strong>.<br/>
                  3. Click <strong>"+ New Access Token"</strong>.<br/>
                  4. Copy the code and paste it below.
                </p>
                <a 
                  href={`${canvasUrl}/profile/settings#access_tokens`} 
                  target="_blank" 
                  rel="noopener noreferrer"
                  className="mt-3 inline-block text-xs font-bold text-[#2d4a5e] underline 
[truncated — 6965 more characters]
```

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