# Project export: Lumora

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 2025
- Tagline: Lumora is a Personal Assistant Marketplace. No need to manually set meetings or write to-do lists any longer! Automatically generate quizzes and flashcards based on your online zoom class!
- Devpost: https://devpost.com/software/lumora
- GitHub: https://github.com/ShaneYokota72/Lumora
- Result: winner (Vercel: Most Creative Use of Vercel/v0 in Edge AI track ($1.2k in Vercel Pro Credits))
- Team: 2 GitHub contributor(s) — Shane Yokota (21 commits), Thor Christoffersen Hochman (14 commits)

## Devpost submission (written by the team)

### Overview

Your AI-Powered Collaboration Hub ✨ What is Lumora? Lumora is a Personal Assistant Marketplace that transforms your Zoom meetings and chats into actionable workflows. By integrating with Zoom's powerful APIs, Lumora orchestrates intelligent workflows to automate tasks, boost productivity, and enhance collaboration—whether you're in education, enterprise, or beyond. 💡 Why the Name Lumora? Lumora represents illuminating collaboration—bringing clarity and energy to your workflows. 🎯 What Inspired Us In our day-to-day lives, we constantly encounter small tasks that add up to consume significant time—scheduling meetings, creating study materials, or researching topics. When these tasks accumulate, they can overwhelm our productivity. Lumora was born from the vision to automate these routine tasks, allowing people to focus on what truly matters. 🚀 What We Built Within 36 hours of hacking, we created a powerful collaboration hub featuring: The Lumora Library We've built 7 specialized tools across education, productivity, and social media: Education Suite: Quiz Generator, Flashcard Creator, Research Assistant Productivity Tools: Meeting Scheduler, Todo List Manager, Timeline Manager Social Media: Tweet Drafter Key Features Comprehensive Zoom Integration: Processes both meeting transcripts and chat messages in real-time Automatically triggers relevant workflows based on conversation context Works seamlessly with the world's leading meeting platform Processes both meeting transcripts and chat messages in real-time Automatically triggers relevant workflows based on conversation context Works seamlessly with the world's leading meeting platform Dynamic Marketplace: Modular architecture enables unlimited tool creation Future potential for thousands of community-created tools Future plans to share and discover tools built by other users Modular architecture enables unlimited tool creation Future potential for thousands of community-created tools Future plans to share and discover tools built by other users Customizable Workspace: Select and activate only the tools you need Mix and match tools for your specific workflow Easily add new tools as your needs evolve Select and activate only the tools you need Mix and match tools for your specific workflow Easily add new tools as your needs evolve Interactive Dashboard: Track completed tasks and take instant actions: Schedule meetings with one click View generated quizzes Access research findings with useful source links Post tweets directly to Twitter/X Schedule meetings with one click View generated quizzes Access research findings with useful source links Post tweets directly to Twitter/X 🔮 What's Next Live AI Tutor/Assistand: Video and voice comes together to help users when they're stuck! Screensharing included! No-Code Tool Creator: Enabling users to build custom workflow tools Expanded Library: Adding more specialized tools for different industries Enhanced Integrations: Supporting more platforms beyond Zoom 🛠️ Tech Stack APIs: Zoom API, Groq API, Perplexity APIs, Calendar API LLM: Groq for orchestration, function calling, and lightning speed inference. Perplexity for deep up-to-date research Frontend: Next.js, Tailwind CSS Backend: Node.js Database: Supabase 📜 License Lumora is open-source under the MIT License. Made with ❤️ by the Lumora Team | Revolutionizing the way we work, one task at a time ✨

## README (from the GitHub repository)

# Lumora 🚀
**Your AI-Powered Collaboration Hub**

## ✨ **What is Lumora?**
Lumora is a Personal Assistant Marketplace that transforms your Zoom meetings and chats into actionable workflows. By integrating with Zoom's powerful APIs, Lumora orchestrates intelligent workflows to automate tasks, boost productivity, and enhance collaboration—whether you're in education, enterprise, or beyond.

## 💡 **Why the Name Lumora?**
Lumora represents illuminating collaboration—bringing clarity and energy to your workflows.

## 🎯 **What Inspired Us**
In our day-to-day lives, we constantly encounter small tasks that add up to consume significant time—scheduling meetings, creating study materials, or researching topics. When these tasks accumulate, they can overwhelm our productivity. Lumora was born from the vision to automate these routine tasks, allowing people to focus on what truly matters.

## 🚀 **What We Built**
Within 36 hours of hacking, we created a powerful collaboration hub featuring:

### **The Lumora Library**
We've built 7 specialized tools across education, productivity, and social media:
- **Education Suite**: Quiz Generator, Flashcard Creator, Research Assistant
- **Productivity Tools**: Meeting Scheduler, Todo List Manager, Timeline Manager
- **Social Media**: Tweet Drafter

### **Key Features**
- **Comprehensive Zoom Integration**: 
  - Processes both meeting transcripts and chat messages in real-time
  - Automatically triggers relevant workflows based on conversation context
  - Works seamlessly with the world's leading meeting platform
- **Dynamic Marketplace**: 
  - Modular architecture enables unlimited tool creation
  - Future potential for thousands of community-created tools
  - Future plans to share and discover tools built by other users
- **Customizable Workspace**: 
  - Select and activate only the tools you need
  - Mix and match tools for your specific workflow
  - Easily add new tools as your needs evolve
- **Interactive Dashboard**: Track completed tasks and take instant actions:
  - Schedule meetings with one click
  - View generated quizzes
  - Access research findings with useful source links
  - Post tweets directly to Twitter/X

## 🔮 **What's Next**
- **Live AI Tutor/Assistand**: Video and voice comes together to help users when they're stuck! Screensharing included!
- **No-Code Tool Creator**: Enabling users to build custom workflow tools
- **Expanded Library**: Adding more specialized tools for different industries
- **Enhanced Integrations**: Supporting more platforms beyond Zoom

## 🛠️ **Tech Stack**
- **APIs**: Zoom API, Groq API, Perplexity APIs, Calendar API
- **LLM**: Groq for orchestration, function calling, and lightning speed inference. Perplexity for deep up-to-date research
- **Frontend**: Next.js, Tailwind CSS
- **Backend**: Node.js
- **Database**: Supabase

## 📜 **License**
Lumora is open-source under the **MIT License**.

---
Made with ❤️ by the Lumora Team | Revolutionizing the way we work, one task at a time ✨


## Detected evidence (automated analysis)

Indexed codebase: 56 recognized source files, 390 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — 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 (67 of 67)

```
.gitignore
lumora_next/.gitignore
lumora_next/components.json
lumora_next/database.types.ts
lumora_next/eslint.config.mjs
lumora_next/next.config.ts
lumora_next/package.json
lumora_next/postcss.config.mjs
lumora_next/README.md
lumora_next/src/app/api/agent/[email]/route.ts
lumora_next/src/app/api/agent/route.ts
lumora_next/src/app/api/lumora-function-call/route.ts
lumora_next/src/app/api/save/route.ts
lumora_next/src/app/api/tasks/[email]/route.ts
lumora_next/src/app/api/todos/[email]/route.ts
lumora_next/src/app/api/todos/route.ts
lumora_next/src/app/globals.css
lumora_next/src/app/home/page.tsx
lumora_next/src/app/layout.tsx
lumora_next/src/app/market/page.tsx
lumora_next/src/app/page.tsx
lumora_next/src/components/Flashcards.tsx
lumora_next/src/components/ParticleBackground.tsx
lumora_next/src/components/Popup.tsx
lumora_next/src/components/Quiz.tsx
lumora_next/src/components/sourcesList.tsx
lumora_next/src/components/Task.tsx
lumora_next/src/components/ui/scroll-area.tsx
lumora_next/src/hooks/useAuth.tsx
lumora_next/src/hooks/useOnClickOutside.ts
lumora_next/src/hooks/usePopup.tsx
lumora_next/src/lib/utils.ts
lumora_next/src/utils/Agent/GroqFunctionCall.ts
lumora_next/src/utils/Agent/types.ts
lumora_next/tailwind.config.ts
lumora_next/tsconfig.json
mockRTMSserver/.dockerignore
mockRTMSserver/.gitignore
mockRTMSserver/data/rtms_credentials.json
mockRTMSserver/docker-compose.yml
mockRTMSserver/Dockerfile
mockRTMSserver/license.md
mockRTMSserver/main.js
mockRTMSserver/package.json
mockRTMSserver/public/css/styles.css
mockRTMSserver/public/index.html
mockRTMSserver/public/js/api.js
mockRTMSserver/public/js/audio-processor.js
mockRTMSserver/public/js/config.js
mockRTMSserver/public/js/mediaHandler.js
mockRTMSserver/public/js/uiController.js
mockRTMSserver/public/js/webSocket.js
mockRTMSserver/readme.md
mockRTMSserver/server/config/serverConfig.js
mockRTMSserver/server/constants/messageTypes.js
mockRTMSserver/server/handlers/mediaHandler.js
mockRTMSserver/server/handlers/signalingHandler.js
mockRTMSserver/server/handlers/webhookHandler.js
mockRTMSserver/server/handlers/wsHandler.js
mockRTMSserver/server/setup/serverSetup.js
mockRTMSserver/server/utils/credentialsManager.js
mockRTMSserver/server/utils/mediaUtils.js
mockRTMSserver/server/utils/wsUtils.js
mockRTMSserver/test_client/client_readme.md
mockRTMSserver/test_client/server.js
mockRTMSserver/test_client/util.js
README.md
```

### Dependencies

- lumora_next/package.json: @eslint/eslintrc@^3, @radix-ui/react-scroll-area@^1.2.3, @supabase/supabase-js@^2.48.1, @types/node@^20, @types/react@^19, @types/react-dom@^19, class-variance-authority@^0.7.1, clsx@^2.1.1, dotenv@^16.4.7, eslint@^9, eslint-config-next@15.1.7, framer-motion@^12.4.3, groq-sdk@^0.15.0, ics@^3.8.1, lucide-react@^0.475.0, next@15.1.7, postcss@^8, react@^19.0.0, react-dom@^19.0.0, react-particles@^2.12.2, tailwind-merge@^3.0.1, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, tsparticles@^3.8.1, tsparticles-engine@^2.12.0, tsparticles-slim@^2.12.0, typescript@^5
- mockRTMSserver/package.json: cors@^2.8.5, dotenv@^16.4.7, express@^4.17.1, multer@^1.4.5-lts.1, node-fetch@^2.6.1, ws@^8.2.3

### Recent commits (newest first)

- fix: readme updated
- Merge pull request #14 from ShaneYokota72/fix/nit
- fix: nit edits
- Merge pull request #13 from ShaneYokota72/researchSourcesFrontend
- Merge branch 'main' into researchSourcesFrontend
- shadcn + research sources frontend
- Merge pull request #12 from ShaneYokota72/imprv/nit-polish
- fix: nit polish
- Merge pull request #11 from ShaneYokota72/ThorWorkingResearchAgent
- Merge branch 'main' into ThorWorkingResearchAgent
- research function
- Merge pull request #10 from ShaneYokota72/imprv/frontend-polish
- imprv: /home ui polished
- Merge pull request #9 from ShaneYokota72/feat/agent-options
- Merge pull request #8 from ShaneYokota72/fix/from-to-issue
- fix: from-to temp solution
- fix: from/to issue temp solution
- Merge pull request #7 from ShaneYokota72/feat/agent-options
- feat: agent customization implemented
- Merge pull request #6 from ShaneYokota72/thorWorking

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

### mockRTMSserver/license.md

```markdown
Copyright 2025 Zoom Communications, Inc

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

```

### mockRTMSserver/test_client/client_readme.md

```markdown
# Zoom RTMS Client

A client to test the RTMS Mock Server. This client connects to the RTMS Mock Server and sends messages to it.

## Overview

This client:
- Listens for Zoom webhook events
- Handles RTMS connection initialization
- Manages WebSocket connections for both signaling and media data
- Processes real-time media streams from RTMS Mock Server

## Prerequisites

- Node.js (v14 or higher)
- npm
- A Zoom account with RTMS enabled
- RTMS credentials (stored in `data/rtms_credentials.json`)

## Setup

1. Install dependencies:
```

## Using ngrok for Webhook Testing

To receive webhooks from the RTMS Mock Server, your client needs to be publicly accessible. ngrok is a useful tool for this:

1. Install ngrok from [https://ngrok.com/download](https://ngrok.com/download)

2. Start ngrok on port 8000:
```bash
ngrok http 8000
```

3. Copy the generated HTTPS URL (e.g., `https://abc123.ngrok.io`)

4. Use this URL as your webhook endpoint in the RTMS Mock Server UI

Note: The ngrok URL changes each time you restart ngrok unless you have a paid account.

## How It Works

1. The client starts an Express server on port 8000 to receive Zoom webhooks
2. When a meeting (session) is started in RTMS Mock Server:
   - Mock server sends a webhook to the client
   - Client establishes a signaling WebSocket connection
   - After successful handshake, connects to media WebSocket
   - Begins receiving real-time media data



## Limitations

This is a minimal implementation focused on core functionality. For production use, consider adding:
- Data processing
```

### mockRTMSserver/docker-compose.yml

```yaml
version: '3.8'

services:
  rtms-mock:
    build: .
    ports:
      - "9092:9092"  # Handshake server
      - "8081:8081"  # Media server
    volumes:
      - ./data:/usr/src/app/data
    environment:
      - NODE_ENV=production
    restart: unless-stopped 
```

### mockRTMSserver/Dockerfile

```
# Use Node.js LTS version
FROM node:18-slim

# Install FFmpeg for media processing
RUN apt-get update && \
    apt-get install -y ffmpeg && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Create app directory
WORKDIR /usr/src/app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy app source
COPY . .

# Create data directory
RUN mkdir -p data

# Expose ports
EXPOSE 9092 8081

# Start the application
CMD ["node", "main.js"] 
```

### mockRTMSserver/package.json

```
{
  "name": "rtms-mock-server",
  "version": "1.0.0",
  "description": "RTMS Mock Server",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/zoom/rtms-mock-server-sample.git"
  },
  "bugs": {
    "url": "https://github.com/zoom/rtms-mock-server-sample/issues"
  },
  "homepage": "https://github.com/zoom/rtms-mock-server-sample#readme",
  "main": "main.js",
  "scripts": {
    "start": "node main.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.4.7",
    "express": "^4.17.1",
    "multer": "^1.4.5-lts.1",
    "node-fetch": "^2.6.1",
    "ws": "^8.2.3"
  }
}

```

### lumora_next/package.json

```
{
  "name": "lumora_next",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@radix-ui/react-scroll-area": "^1.2.3",
    "@supabase/supabase-js": "^2.48.1",

    "class-variance-authority": "^0.7.1",
    
    "clsx": "^2.1.1",
    "dotenv": "^16.4.7",
    "framer-motion": "^12.4.3",
    "groq-sdk": "^0.15.0",
    "ics": "^3.8.1",
    "lucide-react": "^0.475.0",
    "next": "15.1.7",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-particles": "^2.12.2",
    "tailwind-merge": "^3.0.1",
    "tailwindcss-animate": "^1.0.7",
    "tsparticles": "^3.8.1",
    "tsparticles-engine": "^2.12.0",
    "tsparticles-slim": "^2.12.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.1.7",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### mockRTMSserver/main.js

```javascript
const ServerSetup = require('./server/setup/serverSetup');
const WSHandler = require('./server/handlers/wsHandler');
const MediaHandler = require('./server/handlers/mediaHandler');
const CONFIG = require('./server/config/serverConfig');
const express = require('express');
const webhookRouter = require("./server/handlers/webhookHandler");

// Initialize global state
global.isHandshakeServerActive = false;
global.mediaServer = null;
global.signalingWebsocket = null;
global.wss = null;
global.logsWss = null;

// Setup servers
const handshakeServer = ServerSetup.setupHandshakeServer();
const mediaHttpServer = ServerSetup.setupMediaServer();

// Setup WebSocket servers
global.wss = WSHandler.setupWebSocketServer(handshakeServer);
global.isHandshakeServerActive = true;

// Setup media server
function initializeMediaServer() {
    if (!global.mediaServer || global.mediaServer.isClosed) {
        global.mediaServer = MediaHandler.setupMediaServer(mediaHttpServer);
        console.log("Media server initialized");
    }
    return global.mediaServer;
}

// Initial media server setup
initializeMediaServer();

// Handle WebSocket upgrade requests
handshakeServer.on("upgrade", (request, socket, head) => {
    // Ensure media server is initialized before handling upgrade
    initializeMediaServer();
    WSHandler.handleUpgrade(request, socket, head);
});

// Add webhook router
const app = require('express')();
app.use("/", webhookRouter);

console.log("Starting WSS servers...");
```

### mockRTMSserver/test_client/server.js

```javascript
const express = require('express');
const crypto = require('crypto');
const WebSocket = require('ws');
const functionCall = require('./util');
require('dotenv').config()

const app = express();
const PORT = 8000;

// Middleware to parse incoming JSON payloads
app.use(express.json());

// Configuration - Replace with actual values, test values can be found in data/rtms_credentials.json
const ZOOM_SECRET_TOKEN = process.env.SECRET_TOKEN; // Webhook secret for validation 
const CLIENT_SECRET = process.env.CLIENT_SECRET; // Secret key for generating HMAC signatures
// Track active connections
const activeConnections = new Map();

/**
 * Function to generate HMAC signature
 * 
 * @param {string} clientId - The client ID of the RTMS application
 * @param {string} meetingUuid - The UUID of the Zoom meeting
 * @param {string} streamId - The RTMS stream ID
 * @param {string} secret - The secret key used for signing
 * @returns {string} HMAC SHA256 signature
 */
function generateSignature(clientId, meetingUuid, streamId, secret) {
    const message = `${clientId},${meetingUuid},${streamId}`;
    return crypto.createHmac("sha256", secret).update(message).digest("hex");
}

/**
 * Webhook endpoint to receive events (chat) from Zoom
 */

app.post('/chat', async (req, res) => {
    // Print the entire request body
    const textContent = req.body.payload
    const { operator, object } = textContent;
    const { contact_email, message } = object;

    console.log('Received chat message:', operator, contact_email, message);

    // add content for function call
    await functionCall({ 
        messageType: "CHAT",
        data: message,
        from: operator,
        to: contact_email 
    });
    res.send('OK');
});

/**
 * Webhook endpoint to receive events from Zoom
 */
app.post('/meeting', (req, res) => {
    const { event, payload } = req.body;

    // Handle Zoom Webhook validation
    if (event === 'endpoint.url_validation' && payload?.plainToken) {
        
        console.log('Received URL validation request:', {
            event,
            plainToken: payload.plainToken
        });

        const hashForValidate = crypto.createHmac('sha256', ZOOM_SECRET_TOKEN)
            .update(payload.plainToken)
            .digest('hex');
            
        const response = {
            plainToken: payload.plainToken,
            encryptedToken: hashForValidate
        };
        
        console.log('Sending URL validation response:', response);
        return res.json(response);
    }

    // Handle RTMS start event
    if (payload?.event === 'meeting.rtms.started' && payload?.payload?.object) {
        console.log('Received RTMS start event. Full request body:', JSON.stringify(req.body, null, 2));
        
        const {
            clientId,
            payload: {
                payload: {
                    object: { meeting_uuid, rtms_stream_id, server_urls }
                }
            }
        } = req.body;

        console.log('Extracted RTMS details:', {
            clientId,
            meeting_uuid,
            rtms_stream_id,
            server_urls
        });

        connectToRTMSWebSocket(clientId, meeting_uuid, rtms_stream_id, server_urls);
    }

    res.sendStatus(200);
});

/**
 * Connects to the RTMS signaling WebSocket server
 * 
 * @param {string} clientId - The client ID
 * @param {string} meetingUuid - The meeting UUID
 * @param {string} streamId - The RTMS stream ID
 * @param {string} serverUrl - WebSocket URL for signaling server
 */
function connectToRTMSWebSocket(clientId, meetingUuid, streamId, serverUrl) {
    const connectionId = `${meetingUuid}_${streamId}`;
    
    // Close existing connection if any
    if (activeConnections.has(connectionId)) {
        activeConnections.get(connectionId).close();
        activeConnections.delete(connectionId);
    }

    const ws = new WebSocket(serverUrl, { rejectUnauthorized: false });
    activeConnections.set(connectionId, ws);

    ws.on("open", () => {
        const signature = generateSignature(clientId, meetingUuid, streamId, CLIENT_SECRET);
        const handshakeMessage = {
            msg_type: "SIGNALING_HAND_SHAKE_REQ",
            protocol_version: 1,
            meeting_uuid: meetingUuid,
            rtms_stream_id: streamId,
            signature: signature
        };
        ws.send(JSON.stringify(handshakeMessage));
    });

    ws.on("message", (data) => {
        const message = JSON.parse(data);
        
        // Handle different message types
        switch (message.msg_type) {
            case "SIGNALING_HAND_SHAKE_RESP":
                if (message.status_code === "STATUS_OK") {
                    const mediaServerUrl = message.media_server.server_urls.all;
                    connectToMediaWebSocket(mediaServerUrl, clientId, meetingUuid, streamId);
                }
                break;
            case "STREAM_STATE_UPDATE":
                if (message.state === "TERMINATED") {
                    ws.close();
                    activeConnections.delete(connectionId);
                }
                break;
            case "KEEP_ALIVE_REQ":
                ws.send(JSON.stringify({
                    msg_type: "KEEP_ALIVE_RESP",
                    timestamp: Date.now()
                }));
                break;
        }
    });

    ws.on("close", () => {
        activeConnections.delete(connectionId);
    });
}

/**
 * Connects to the Media WebSocket server
 * 
 * @param {string} endpoint - WebSocket URL for media server
 * @param {string} clientId - The client ID
 * @param {string} meetingUuid - The meeting UUID
 * @param {string} streamId - The RTMS stream ID
 */
function connectToMediaWebSocket(endpoint, clientId, meetingUuid, streamId) {
    const connectionId = `${meetingUuid}_${streamId}_media`;
    
    // Close existing media connection if any
    if (activeConnections.has(connectionId)) {
        activeConnections.get(connectionId).close();
        activeConnections.delete(con
[truncated — 1588 more characters]
```

### lumora_next/src/app/layout.tsx

```typescript
import type React from "react"
import { Inter } from "next/font/google"
import "./globals.css"
import { ProvideAuth } from "@/hooks/useAuth";
import { ProvidePopup } from "@/hooks/usePopup";

const inter = Inter({ subsets: ["latin"], weight: ["300", "400"] })

export const metadata = {
  title: "Lumora - AI-Powered Collaboration Hub",
  description: "Transform your meetings and chats into actionable workflows with Lumora",
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className="dark">
      <ProvideAuth>
        <ProvidePopup>
          <body className={`${inter.className} bg-gray-900 text-gray-100 font-light`}>
            <main className="min-h-screen">{children}</main>
          </body>
        </ProvidePopup>
      </ProvideAuth>
    </html>
  )
}


```

### lumora_next/src/app/page.tsx

```typescript
'use client'

import Link from "next/link"
import { motion } from "framer-motion"
import { Rocket, Zap, Cog } from "lucide-react"
// import dynamic from "next/dynamic"

// const ParticleBackground = dynamic(() => import("../components/ParticleBackground.tsx"), { ssr: false })

export default function LandingPage() {
  return (
    <div className="min-h-screen flex flex-col justify-center items-center p-8 relative overflow-hidden">
      {/* <ParticleBackground /> */}
      <motion.div
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.8 }}
        className="text-center z-10"
      >
        <h1 className="text-5xl font-light mb-4 text-gray-100">
          Welcome to <span className="accent-text font-normal">Lumora</span>
        </h1>
        <p className="text-xl mb-8 text-gray-300 font-light">Your AI-Powered Collaboration Hub</p>
        <Link
          href="/home"
          className="bg-purple-600 hover:bg-purple-700 text-white font-light py-2 px-6 rounded-md transition duration-300 shadow-lg hover:shadow-xl"
        >
          Get Started
        </Link>
      </motion.div>

      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ delay: 0.5, duration: 0.8 }}
        className="mt-16 grid grid-cols-1 md:grid-cols-3 gap-8 z-10"
      >
        <FeatureCard
          icon={<Rocket className="w-6 h-6 mb-4 text-purple-400" />}
          title="AI Agents"
          description="Automate tasks with intelligent agents"
        />
        <FeatureCard
          icon={<Zap className="w-6 h-6 mb-4 text-purple-400" />}
          title="Zoom Integration"
          description="Transform meetings into actionable workflows"
        />
        <FeatureCard
          icon={<Cog className="w-6 h-6 mb-4 text-purple-400" />}
          title="Customizable"
          description="Tailor agents to your specific needs"
        />
      </motion.div>
    </div>
  )
}

function FeatureCard({ 
  icon, 
  title, 
  description 
}: {
  icon: React.ReactNode
  title: string
  description: string
}) {
  return (
    <motion.div
      whileHover={{ scale: 1.05 }}
      className="bg-gray-800 bg-opacity-50 p-6 rounded-lg accent-border shadow-lg backdrop-blur-sm"
    >
      {icon}
      <h2 className="text-xl font-light mb-2 text-gray-200">{title}</h2>
      <p className="text-gray-400 text-sm font-light">{description}</p>
    </motion.div>
  )
}


```

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