# Project export: Watchdog

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

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Help us make our schools safer.
- Devpost: https://devpost.com/software/watchdog-2vwqlf
- GitHub: https://github.com/oviozz/WatchDog-2024-Hackathon-Berkeley
- Result: winner (Hyperbolic: AI Inference Application Bounty)
- Team: 1 GitHub contributor(s) — bazalu (3 commits)

## Devpost submission (written by the team)

### Overview

What inspired us to build it Guns are now the leading cause of death among American children and teens, with 1 in every 10 gun deaths occurring in individuals aged 19 or younger. School shootings, in particular, have become a tragic epidemic in the U.S., underscoring the urgent need for enhanced safety measures. Our team united with a shared vision to leverage AI technology to improve security in American schools, helping to protect children and ensure their safety.

### What it does

Our product leverages advanced AI technology to enhance school safety by detecting potential threats in real-time. By streaming surveillance footage, our AI system can identify weapons, providing instant alerts to security personnel and administrators. In addition to visual monitoring, we integrate audio streaming to analyze changes in sentiment, such as raised voices or signs of distress. This dual approach—combining visual and auditory cues—enables rapid response to emerging threats.

### How we built it

We partnered with incredible sponsors—Deepgram, Hyperbolic, Groq, and Fetch.AI—to develop a comprehensive security solution that uses cutting-edge AI technologies. With their support, we were able to conduct fast AI inference, deploy an emergency contact agent, and create intelligent systems capable of tracking potential threats and key variables, all to ensure the safety of our communities. For real-time data processing, we utilized Firebase and Convex to enable rapid write-back and retrieval of critical information. Additionally, we trained our weapon detection agent using Ultralytics YOLO v8 on the Roboflow platform, achieving an impressive ~90% accuracy. This high-performance detection system, combined with AI-driven analytics, provides a robust safety infrastructure capable of identifying and responding to threats in real time.

### Challenges we ran into

Streaming a real-time AI object detection model with both low latency and high accuracy was a significant challenge. Initially, we experimented with Flask and FastAPI for serving our model, followed by trying AWS and Docker to improve performance. However, after further optimization efforts, we ultimately integrated Roboflow.js directly in the browser using a Native SDK. This approach gave us a substantial advantage, allowing us to run the model efficiently within the client environment. As a result, we achieved the ability to track weapons quickly and accurately in real time, meeting the critical demands of our security solution.

### Accomplishments we're proud of

We are incredibly proud of the features our product offers, providing a comprehensive and fully integrated security experience. Beyond detecting weapons and issuing instant alerts to law enforcement, faculty, and students through AI-powered agents, we also implemented extensive sentiment analysis. This enables us to detect emotional escalations that may signal potential threats. All of this is supported by real-time security data displays, ensuring that key decision-makers are always informed with up-to-the-minute information. Our system seamlessly brings together cutting-edge AI and real-time data processing to deliver a robust, proactive security solution.

### What we learned

We learned that the night is darkest right before the dawn... and that we need to persevere and be steadfast as a team to see our vision come to fruition.

### What's next

We want to get incorporated in the American school system!

## README (from the GitHub repository)

# 🛡️ Watchdog: AI-Powered School Safety System

## Images

**Weapon Detection**
![image](https://github.com/user-attachments/assets/32f33874-5178-4714-b463-b8d0b7773938)
**Alert System**
![image](https://github.com/user-attachments/assets/54bd45f7-bb25-48e7-8fad-921ca3f0f56b)
**Police Report**
![image](https://github.com/user-attachments/assets/98cb0faa-9535-437f-80f2-191a4161c948)
**Real-time voice analysis**
![image](https://github.com/user-attachments/assets/62ca2428-68cd-4ee6-bae9-02292b43447d)
![image](https://github.com/user-attachments/assets/729caff5-40ac-4ad1-ac40-7c7328230e11)



## 🚀 Inspiration
Gun violence is now the leading cause of death among American children and teens, with 1 in every 10 gun fatalities involving individuals aged 19 or younger. In the U.S., school shootings have escalated into a tragic epidemic, underscoring the urgent need for enhanced security in schools. United by a shared vision, our team leveraged AI technology to create a platform aimed at improving school safety, protecting children, and giving peace of mind to students, parents, and educators alike.

## 🎯 What It Does
Watchdog uses advanced AI to enhance school security by detecting threats in real-time. Our platform streams live surveillance footage, leveraging AI to identify weapons and other indicators of potential violence, and issues instant alerts to security personnel and administrators. Additionally, our audio streaming feature detects sentiment changes, such as raised voices or distress signals, allowing security teams to respond rapidly to unfolding threats. By combining visual and auditory cues, Watchdog offers a comprehensive and proactive approach to school safety.

## 🛠️ How We Built It
With the support of our incredible sponsors—Deepgram, Hyperbolic, Groq, and Fetch.AI—we developed a robust AI security solution. Here’s an overview of our tech stack and approach:

- **Real-Time Data Processing**: Integrated Firebase and Convex for rapid data retrieval and write-back to facilitate real-time communication and alerts.
- **Weapon Detection**: Trained an agent using Ultralytics YOLO v8 on the Roboflow platform, achieving ~90% accuracy in weapon detection.
- **Audio Analysis**: Implemented sentiment analysis through Deepgram’s API to detect audio patterns like raised voices, signaling potential distress.
- **Model Deployment**: We experimented with Flask and FastAPI for model serving and explored AWS and Docker to optimize performance. Ultimately, we chose to implement Roboflow.js directly in the browser with a Native SDK, enabling fast and efficient model inference on the client side.

## 🧩 Challenges We Encountered
Achieving low latency and high accuracy with real-time AI object detection presented challenges, especially during initial model deployment. We experimented with various backends and infrastructures but ultimately found that running the model directly in the browser yielded optimal results. By leveraging Roboflow.js's Native SDK, we achieved efficient tracking with fast response times, meeting the stringent performance requirements of our safety solution.

## 🏆 Key Accomplishments
We’re proud of the capabilities we built into Watchdog:

- **Instant Weapon Detection**: Our AI agents quickly detect weapons and alert law enforcement, faculty, and students in real-time.
- **Sentiment Analysis**: Integrated sentiment detection to identify emotional escalations that could indicate potential threats.
- **Comprehensive Data Display**: We developed real-time data dashboards that provide school officials and security teams with up-to-the-minute information, helping them make informed decisions swiftly.
- **Integrated AI-Driven Safety Solution**: Bringing together visual and auditory detection in a unified system, Watchdog offers a robust, proactive approach to school security.

## 💡 What We Learned
Building Watchdog reinforced our belief in the power of perseverance. We learned that each challenge pushed us closer to our goal, and that by standing firm as a team, we could achieve our vision of a safer learning environment.

## 🚀 What's Next for Watchdog
Our ultimate vision is to see Watchdog incorporated into the American school system, providing a safer educational environment across the nation.

---

### 🛠️ Built With
- **AI & Machine Learning**: Ultralytics YOLO v8, Roboflow
- **Audio Analysis**: Deepgram API
- **Data Management**: Firebase, Convex
- **Backend**: Roboflow.js Native SDK, Flask, FastAPI, AWS, Docker
- **Real-Time Streaming & Dashboard**: Custom JavaScript integrations with Convex

---


## Detected evidence (automated analysis)

Indexed codebase: 51 recognized source files, 122 KB.
- CSS (language) — detected in the code
- Firebase (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Express (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (60 of 60)

```
.eslintrc.json
.gitignore
.idea/.gitignore
.idea/inspectionProfiles/Project_Default.xml
.idea/modules.xml
.idea/vcs.xml
.idea/watchdog.iml
components.json
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/imageAnalysis.js
convex/schema.js
convex/securityAlert.js
jsconfig.json
next.config.mjs
package.json
postcss.config.mjs
README.md
src/app/(dashboard)/_components/left/ActionsButtons.jsx
src/app/(dashboard)/_components/left/BuildingInfo.jsx
src/app/(dashboard)/_components/left/EmergencyResources.jsx
src/app/(dashboard)/_components/left/RecentThreats.jsx
src/app/(dashboard)/_components/left/ThreatIndicator.jsx
src/app/(dashboard)/_components/LeftSide.jsx
src/app/(dashboard)/_components/right/BuildingMap.jsx
src/app/(dashboard)/_components/right/LiveCameraDashboard.jsx
src/app/(dashboard)/_components/right/LiveCameraFeed.jsx
src/app/(dashboard)/_components/right/SecurityLog.jsx
src/app/(dashboard)/_components/RightSide.jsx
src/app/(dashboard)/page.jsx
src/app/api/deepgram/route.js
src/app/api/transcribe/route.js
src/app/cctv/page.jsx
src/app/cctv/WeaponObjectDetectionVideo.jsx
src/app/globals.css
src/app/layout.js
src/app/voice/layout.js
src/app/voice/page.jsx
src/app/voice/SecurityAudioAnalysis.jsx
src/app/WeaponObjectDetectionVideo.css
src/assests/fonts.js
src/components/AudioVisualizerDesign.jsx
src/components/AudioVisualizerIndicator.jsx
src/components/layout/navbar.jsx
src/components/ui/alert.jsx
src/components/ui/badge.jsx
src/components/ui/button.jsx
src/components/ui/card.jsx
src/components/ui/dialog.jsx
src/components/ui/resizable.jsx
src/components/WeaponAlertDialog.jsx
src/lib/groq/config.js
src/lib/utils.js
src/providers/ConvexClientProvider.jsx
src/providers/DeepgramContextProvider.jsx
src/providers/MicrophoneContextProvider.jsx
tailwind.config.js
```

### Dependencies

- package.json: @deepgram/sdk@^3.8.1, @radix-ui/react-dialog@^1.1.2, @radix-ui/react-icons@^1.3.0, @radix-ui/react-slot@^1.1.0, @tensorflow-models/coco-ssd@^2.2.3, @tensorflow/tfjs@^4.21.0, @uploadthing/react@^7.0.3, class-variance-authority@^0.7.0, clsx@^2.1.1, convex@^1.16.6, cross-fetch@^4.0.0, dotenv@^16.4.5, eslint@^8, eslint-config-next@14.2.15, firebase@^10.14.1, firebase-admin@^12.6.0, framer-motion@^11.11.9, inferencejs@^1.0.14, lucide-react@^0.453.0, moment@^2.30.1, next@14.2.15, node-fetch@^3.3.2, nprogress@^0.2.0, openai@^4.68.1, postcss@^8, react@^18, react-dom@^18, react-force-graph-2d@^1.25.7, react-icons@^5.3.0, react-resizable-panels@^2.1.4, react-webcam@^7.2.0, recharts@^2.13.0, tailwind-merge@^2.5.4, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, uploadthing@^7.1.0, ws@^8.18.0

### Recent commits (newest first)

- Update README.md
- Update README.md
- Update README.md
- updated
- first commit
- first commit
- Initial commit from Create Next App

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

### package.json

```
{
  "name": "watchdog",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@deepgram/sdk": "^3.8.1",
    "@radix-ui/react-dialog": "^1.1.2",
    "@radix-ui/react-icons": "^1.3.0",
    "@radix-ui/react-slot": "^1.1.0",
    "@tensorflow-models/coco-ssd": "^2.2.3",
    "@tensorflow/tfjs": "^4.21.0",
    "@uploadthing/react": "^7.0.3",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "convex": "^1.16.6",
    "cross-fetch": "^4.0.0",
    "dotenv": "^16.4.5",
    "firebase": "^10.14.1",
    "firebase-admin": "^12.6.0",
    "framer-motion": "^11.11.9",
    "inferencejs": "^1.0.14",
    "lucide-react": "^0.453.0",
    "moment": "^2.30.1",
    "next": "14.2.15",
    "nprogress": "^0.2.0",
    "openai": "^4.68.1",
    "react": "^18",
    "react-dom": "^18",
    "react-force-graph-2d": "^1.25.7",
    "react-icons": "^5.3.0",
    "react-resizable-panels": "^2.1.4",
    "react-webcam": "^7.2.0",
    "recharts": "^2.13.0",
    "tailwind-merge": "^2.5.4",
    "tailwindcss-animate": "^1.0.7",
    "uploadthing": "^7.1.0",
    "ws": "^8.18.0"
  },
  "devDependencies": {
    "eslint": "^8",
    "eslint-config-next": "14.2.15",
    "node-fetch": "^3.3.2",
    "postcss": "^8",
    "tailwindcss": "^3.4.1"
  }
}

```

### src/app/layout.js

```javascript

import "./globals.css";
import {cn} from "@/lib/utils";
import Navbar from "@/components/layout/navbar";
import {poppins} from "@/assests/fonts";
import {ConvexClientProvider} from "@/providers/ConvexClientProvider";
import WeaponAlertDialog from "@/components/WeaponAlertDialog";


export const metadata = {
    title: "Create Next App",
    description: "Generated by create next app",
};

export default function RootLayout({children}) {
    return (
        <html lang="en">
            <body className={cn("antialiased bg-gray-900 text-white", poppins.className)}>
                <ConvexClientProvider>
                    <main className={"px-4 flex flex-col"}>
                        <Navbar />
                        {children}
                        <WeaponAlertDialog />
                    </main>
                </ConvexClientProvider>
            </body>
        </html>
    );
}

```

### convex/_generated/server.js

```javascript
/* prettier-ignore-start */

/* eslint-disable */
/**
 * Generated utilities for implementing server-side Convex query and mutation functions.
 *
 * THIS CODE IS AUTOMATICALLY GENERATED.
 *
 * To regenerate, run `npx convex dev`.
 * @module
 */

import {
  actionGeneric,
  httpActionGeneric,
  queryGeneric,
  mutationGeneric,
  internalActionGeneric,
  internalMutationGeneric,
  internalQueryGeneric,
} from "convex/server";

/**
 * Define a query in this Convex app's public API.
 *
 * This function will be allowed to read your Convex database and will be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const query = queryGeneric;

/**
 * Define a query that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to read from your Convex database. It will not be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const internalQuery = internalQueryGeneric;

/**
 * Define a mutation in this Convex app's public API.
 *
 * This function will be allowed to modify your Convex database and will be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const mutation = mutationGeneric;

/**
 * Define a mutation that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to modify your Convex database. It will not be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const internalMutation = internalMutationGeneric;

/**
 * Define an action in this Convex app's public API.
 *
 * An action is a function which can execute any JavaScript code, including non-deterministic
 * code and code with side-effects, like calling third-party services.
 * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
 * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
 *
 * @param func - The action. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
 */
export const action = actionGeneric;

/**
 * Define an action that is only accessible from other Convex functions (but not from the client).
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
 */
export const internalAction = internalActionGeneric;

/**
 * Define a Convex HTTP action.
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
 * as its second.
 * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
 */
export const httpAction = httpActionGeneric;

/* prettier-ignore-end */

```

### src/app/cctv/page.jsx

```javascript


import WeaponObjectDetectionVideo from "@/app/cctv/WeaponObjectDetectionVideo";
import Script from "next/script";


export default async function Page(){

    return (
        <>
            <Script
                src={"https://cdn.roboflow.com/0.2.26/roboflow.js"}
                strategy="beforeInteractive"
            />

            <WeaponObjectDetectionVideo />
        </>
    )
}
```

### src/app/voice/layout.js

```javascript

import {MicrophoneContextProvider} from "@/providers/MicrophoneContextProvider";
import {DeepgramContextProvider} from "@/providers/DeepgramContextProvider";

export default function Layout({children}){

    return (
        <div>
            <MicrophoneContextProvider>
                <DeepgramContextProvider>{children}</DeepgramContextProvider>
            </MicrophoneContextProvider>
        </div>
    )


}
```

### src/app/(dashboard)/page.jsx

```javascript

import LeftSide from "@/app/(dashboard)/_components/LeftSide";
import RightSide from "@/app/(dashboard)/_components/RightSide";
import Script from "next/script";

export default function Dashboard() {
    return (
        <div className={"flex xl:flex-row flex-col w-full bg-gray-800 h-screen p-5 rounded-xl"}>
            <div className="xl:w-1/2 w-full pr-2">
                <LeftSide />
            </div>
            <div className="w-full pl-2">
                <RightSide />
            </div>
        </div>
    );
}

```

### src/app/voice/page.jsx

```javascript

"use client"

import { useEffect, useRef, useState } from "react";
import { LiveConnectionState, LiveTranscriptionEvents, useDeepgram } from "@/providers/DeepgramContextProvider";
import { MicrophoneEvents, MicrophoneState, useMicrophone } from "@/providers/MicrophoneContextProvider";
import AudioVisualizerIndicator from "@/components/AudioVisualizerIndicator";
import SecurityAudioAnalysis from "@/app/voice/SecurityAudioAnalysis";

const VoicePage = () => {

    const [captions, setCaptions] = useState([]);
    const [isStreaming, setIsStreaming] = useState(false);
    const [currentStreamingText, setCurrentStreamingText] = useState("");
    const [isHovering, setIsHovering] = useState(false);
    const { connection, connectToDeepgram, connectionState } = useDeepgram();
    const { setupMicrophone, microphone, startMicrophone, microphoneState } = useMicrophone();
    const keepAliveInterval = useRef();
    const containerRef = useRef(null);

    // Simulated audio data for visualization
    const [audioData, setAudioData] = useState(Array(50).fill(50));

    useEffect(() => {
        // Simulate audio visualization
        const interval = setInterval(() => {
            setAudioData(prev => prev.map(() => Math.random() * 80 + 10));
        }, 100);

        return () => clearInterval(interval);
    }, []);

    useEffect(() => {
        setupMicrophone();
    }, []);

    useEffect(() => {
        if (microphoneState === MicrophoneState.Ready) {
            connectToDeepgram({
                model: "nova-2",
                interim_results: true,
                smart_format: true,
                filler_words: true,
                utterance_end_ms: 3000,
            });
        }
    }, [microphoneState]);

    const streamText = async (text) => {
        setIsStreaming(true);
        let currentText = "";
        const words = text.split(" ");

        for (let word of words) {
            currentText += word + " ";
            setCurrentStreamingText(currentText);
            await new Promise(resolve => setTimeout(resolve, 50));
        }

        setIsStreaming(false);
        setCurrentStreamingText("");
        setCaptions(prev => [...prev, text]);
    };

    useEffect(() => {
        if (!microphone || !connection) return;

        const onData = (e) => {
            if (e.data.size > 0) {
                connection?.send(e.data);
            }
        };

        const onTranscript = (data) => {
            const thisCaption = data.channel.alternatives[0].transcript;
            if (thisCaption.trim() !== "") {
                streamText(thisCaption);
            }
        };

        if (connectionState === LiveConnectionState.OPEN) {
            connection.addListener(LiveTranscriptionEvents.Transcript, onTranscript);
            microphone.addEventListener(MicrophoneEvents.DataAvailable, onData);
            startMicrophone();
        }

        return () => {
            connection?.removeListener(LiveTranscriptionEvents.Transcript, onTranscript);
            microphone?.removeEventListener(MicrophoneEvents.DataAvailable, onData);
        };
    }, [connectionState]);

    useEffect(() => {
        if (containerRef.current) {
            containerRef.current.scrollTop = containerRef.current.scrollHeight;
        }
    }, [captions, currentStreamingText]);

    return (
        <div className="relative flex h-screen antialiased">
            <div className="flex flex-col h-full w-full overflow-x-hidden relative">

                <div className="flex-1">
                    <SecurityAudioAnalysis fullText={captions}/>
                </div>

                {/* Caption container fixed at bottom */}
                <div className="fixed bottom-0 left-0 right-0 p-4 rounded-t-2xl bg-gray-800 border-white-2 backdrop-blur-sm shadow-lg">
                    <div className="flex items-center justify-between mb-4">
                        <h2
                            className={`text-2xl font-bold text-purple-200 transition-all duration-300 ease-in-out ${
                                isHovering ? 'scale-110' : ''
                            }`}
                            style={{
                                textShadow: isHovering ? '0 0 10px #e9d5ff' : 'none',
                            }}
                            onMouseEnter={() => setIsHovering(true)}
                            onMouseLeave={() => setIsHovering(false)}
                        >
                            Audio Transcription
                        </h2>
                        {connectionState === LiveConnectionState.OPEN && (
                            <div className="text-green-400 font-medium animate-pulse">
                                Hearing you speak...
                            </div>
                        )}
                    </div>

                    <div className="h-24 bg-purple-200/50 rounded-md overflow-hidden mb-4">
                        <AudioVisualizerIndicator />
                    </div>

                    <div
                        ref={containerRef}
                        className="bg-purple-800/50 p-4 rounded-md mb-4 max-h-[200px] overflow-y-auto"
                    >
                        <div className="flex items-center flex-wrap gap-2">
                            {captions.map((caption, index) => (
                                <p key={index} className="text-md text-purple-100">
                                    {caption}
                                </p>
                            ))}
                            {isStreaming && (
                                <p className="text-purple-100">
                                    {currentStreamingText}
                                    <span className="animate-pulse">▊</span>
                                </p>
                            )}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default Vo
[truncated — 8 more characters]
```

### src/app/api/deepgram/route.js

```javascript

import { NextResponse } from 'next/server';

export async function GET() {
    const apiKey = process.env.DEEPGRAM_API_KEY;

    if (!apiKey) {
        return NextResponse.json({ error: 'Deepgram API key not found' }, { status: 500 });
    }

    return NextResponse.json({ key: apiKey });
}
```

### src/app/api/transcribe/route.js

```javascript

import { createClient, LiveTranscriptionEvents } from '@deepgram/sdk';
import { Server } from 'ws';

const deepgram = createClient(process.env.NEXT_DEEPGRAM_API_KEY);

const handler = (req, res) => {
    if (req.method === 'GET') {
        res.status(200).send('WebSocket server is running.');
    } else {
        res.status(405).end(); // Method Not Allowed
    }
};

// Create WebSocket server
const wss = new Server({ noServer: true });

wss.on('connection', (ws) => {
    const connection = deepgram.listen.live({
        model: 'nova-2',
        language: 'en-US',
        smart_format: false,
    });

    connection.on(LiveTranscriptionEvents.Open, () => {
        console.log('WebSocket connection opened.');
    });

    connection.on(LiveTranscriptionEvents.Transcript, (data) => {
        const transcript = data.channel.alternatives[0].transcript;
        ws.send(transcript);
    });

    connection.on(LiveTranscriptionEvents.Close, () => {
        console.log('WebSocket connection closed.');
    });

    connection.on(LiveTranscriptionEvents.Error, (err) => {
        console.error(err);
    });

    ws.on('message', (message) => {
        // Handle incoming audio data
        connection.send(message);
    });

    ws.on('close', () => {
        connection.close();
    });
});

// Add the WebSocket server to the Next.js API handler
export default (req, res) => {
    if (req.method === 'GET') {
        res.socket.server.on('upgrade', (request, socket, head) => {
            wss.handleUpgrade(request, socket, head, (ws) => {
                wss.emit('connection', ws, request);
            });
        });
        return handler(req, res);
    }
    return handler(req, res);
};

```

### tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
    darkMode: ["class"],
    content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
  	extend: {
  		colors: {
  			background: 'hsl(var(--background))',
  			foreground: 'hsl(var(--foreground))',
  			card: {
  				DEFAULT: 'hsl(var(--card))',
  				foreground: 'hsl(var(--card-foreground))'
  			},
  			popover: {
  				DEFAULT: 'hsl(var(--popover))',
  				foreground: 'hsl(var(--popover-foreground))'
  			},
  			primary: {
  				DEFAULT: 'hsl(var(--primary))',
  				foreground: 'hsl(var(--primary-foreground))'
  			},
  			secondary: {
  				DEFAULT: 'hsl(var(--secondary))',
  				foreground: 'hsl(var(--secondary-foreground))'
  			},
  			muted: {
  				DEFAULT: 'hsl(var(--muted))',
  				foreground: 'hsl(var(--muted-foreground))'
  			},
  			accent: {
  				DEFAULT: 'hsl(var(--accent))',
  				foreground: 'hsl(var(--accent-foreground))'
  			},
  			destructive: {
  				DEFAULT: 'hsl(var(--destructive))',
  				foreground: 'hsl(var(--destructive-foreground))'
  			},
  			border: 'hsl(var(--border))',
  			input: 'hsl(var(--input))',
  			ring: 'hsl(var(--ring))',
  			chart: {
  				'1': 'hsl(var(--chart-1))',
  				'2': 'hsl(var(--chart-2))',
  				'3': 'hsl(var(--chart-3))',
  				'4': 'hsl(var(--chart-4))',
  				'5': 'hsl(var(--chart-5))'
  			}
  		},
  		borderRadius: {
  			lg: 'var(--radius)',
  			md: 'calc(var(--radius) - 2px)',
  			sm: 'calc(var(--radius) - 4px)'
  		}
  	}
  },
  plugins: [require("tailwindcss-animate")],
};

```

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