# Project export: Slumber Synthesizer

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: Record, analyze, and explore your dreams in a beautiful and secure environment
- Devpost: https://devpost.com/software/slumber-synthesizer
- GitHub: https://github.com/MiPlayer123/slumber-synthesizer
- Demo: https://slumber-synthesizer.lovable.app/
- Video: https://www.youtube.com/embed/E1teSDidmEI?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — gpt-engineer-app[bot] (21 commits), Sibi Raja (15 commits), Mikul Saravanan (4 commits), Tom Zhang (3 commits)

## Devpost submission (written by the team)

### Overview

Core Features Slumber Synthesizer combines personal dream journaling with AI-powered analysis and community features, creating an engaging platform for recording and exploring dreams while maintaining user privacy when desired. It adds a fun way to share your silly dreams with friends worldwide while cataloging all of your dreams and analytics privately. 1. Dream Recording Record personal dreams with titles and descriptions Categorize dreams (nightmare, lucid, recurring, prophetic, normal) Add emotional context (joy, fear, confusion, anxiety, peace, etc.) Get AI-generated dream imagery Receive AI-enhanced descriptions and interpretations 2. Community Sharing Share dreams publicly with other users Browse and explore dreams shared by the community View AI-generated dream images from other users Keep dreams private or make them public 3. Analytics Track dream patterns over time View emotional and category distributions See total dreams recorded Analyze weekly dream frequency Visual charts and statistics 4. User Features Secure authentication Personal dream journal Profile management Privacy controls Responsive design for all devices What Inspired Us The inspiration for our project, Slumber Synthesizer, came from a shared fascination for understanding dreams and how they affect sleep. Dreams often hold untapped insights into our subconscious, yet they are fleeting and difficult to analyze. We wanted to create a platform where users could not only record their dreams but also explore patterns, themes, and meanings in a secure and engaging environment. What We Learned Throughout this journey, we gained valuable insights into both technical and non-technical aspects of building a web app. Technical Insights: Deepened our understanding of natural language processing (NLP) for analyzing dream patterns and themes. Learned how to design an intuitive user interface that balances functionality with aesthetic appeal. Technical Insights: Deepened our understanding of natural language processing (NLP) for analyzing dream patterns and themes. Learned how to design an intuitive user interface that balances functionality with aesthetic appeal. Non-Technical Insights: Discovered the importance of user feedback in shaping features. Ensured accessibility for diverse audiences. Non-Technical Insights: Discovered the importance of user feedback in shaping features. Ensured accessibility for diverse audiences. How We Built It Frontend Framework React with TypeScript Chosen for its robust ecosystem, type safety, and component-based architecture Used throughout the application for building reusable UI components TypeScript adds static typing, improving code reliability and developer experience Chosen for its robust ecosystem, type safety, and component-based architecture Used throughout the application for building reusable UI components TypeScript adds static typing, improving code reliability and developer experience UI Components & Styling Shadcn/UI Provides high-quality, accessible React components Used for buttons, cards, forms, and other UI elements Ensures consistent design language across the application Provides high-quality, accessible React components Used for buttons, cards, forms, and other UI elements Ensures consistent design language across the application Tailwind CSS Utility-first CSS framework for responsive design Used for custom styling and layouts Enables rapid UI development with minimal CSS overhead Utility-first CSS framework for responsive design Used for custom styling and layouts Enables rapid UI development with minimal CSS overhead Framer Motion Animation library for React Implements smooth transitions and animations Used in landing page and UI interactions for better user experience Also used Toast for status updates Animation library for React Implements smooth transitions and animations Used in landing page and UI interactions for better user experience Also used Toast for status updates State Management & Data Fetching TanStack Query (React Query) Manages server state and data fetching Handles caching and synchronization of dream data Used in Community and Journal pages for efficient data loading Manages server state and data fetching Handles caching and synchronization of dream data Used in Community and Journal pages for efficient data loading Backend & Database Supabase Provides backend-as-a-service functionality Used for: User authentication and authorization PostgreSQL database for storing dreams and user profiles Storage bucket for dream-related images Edge Functions for AI integration Provides backend-as-a-service functionality Used for: User authentication and authorization PostgreSQL database for storing dreams and user profiles Storage bucket for dream-related images Edge Functions for AI integration AI Integration OpenAI's DALL-E 3 Generates dream-inspired images Integrated via Supabase Edge Functions Generates dream-inspired images Integrated via Supabase Edge Functions Mistral AI Enhances dream descriptions Provides AI-powered dream analysis Also integrated through Edge Functions Enhances dream descriptions Provides AI-powered dream analysis Also integrated through Edge Functions Routing & Navigation React Router Handles client-side routing Manages navigation between different sections of the app Used for creating a seamless single-page application experience Handles client-side routing Manages navigation between different sections of the app Used for creating a seamless single-page application experience Development Tools Vite Modern build tool for faster development Provides quick hot module replacement Optimizes production builds Modern build tool for faster development Provides quick hot module replacement Optimizes production builds Each tool was chosen with consideration for the developer experience, performance, maintainability, and scalability. This stack provides a modern, robust foundation for building high-quality web applications while maintaining excellent developer experience and end-user performance. Challenges We Faced Building Slumber Synthesizer was not without its challenges: NLP Complexity Designing AI models capable of accurately analyzing subjective dream content required extensive research and iterative fine-tuning. Data Privacy Ensuring that user data remained secure and private was a top priority, which involved choosing the correct tools and implementing robust encryption protocols. User Engagement Creating a platform that felt both personal and community-driven required careful balancing of features to cater to individual users while fostering meaningful interactions within the community. Time Constraints As with any hackathon project, time was limited, so we had to prioritize features while maintaining a high-quality user experience. Despite these challenges, we are proud of what we accomplished in such a short time frame. Slumber Synthesizer is not just a tool for journaling dreams but a gateway to self-discovery and connection with others who share an interest in exploring the subconscious mind. Leveraging Perplexity for Development During our coding and development process, we used Perplexity as a valuable tool to streamline our workflow. Perplexity provided detailed explanations, troubleshooting tips, and best practices, which significantly improved our efficiency. It helped us in various areas: Research and Brainstorming: Offered quick insights on integrating new libraries and optimizing our codebase. Debugging Assistance: Provided code examples and tips to resolve errors in real-time. Documentation Support: Assisted in producing clear and concise documentation for our project. Time Efficiency: Accelerated our development process by reducing the time spent on repetitive research and troubleshooting. Market Potential Slumber Synthesizer isn’t just a dream journaling app—it’s a platform built for real-world impact. We’re addressing a significant personal wellness and mental health opportunity by enabling users to capture, analyze, and learn from their dreams. It also adds a fun way to share your silly dreams with friends around the world. Our product offers a personal wellness tool for self-reflection and incorporates a social media element—allowing users to share their dreams and interpretations within a vibrant community. There are over 4.7 billion social media users globally. If we capture just 0.1% of this vast audience, that’s roughly 4.7 million potential users. In addition, the digital mental wellness and self-improvement market is estimated at around $3–$4 billion today, with projections to reach $10 billion or more in the next 5–10 years. Assuming a conservative premium conversion rate of 5–10% among engaged users, capturing 0.1% of social media users could yield 235,000 to 470,000 premium subscribers. With an average revenue per user (ARPU) of $20 per year for premium features, this translates to an annual revenue potential of roughly $4.7M to $9.4M While our current implementation includes essential social sharing features, this is just the base. We plan to expand this aspect further by introducing personalized feeds, interactive community challenges, and integrations with popular social platforms, ensuring users remain engaged and inspired. We are committed to the long-term vision of continuous innovation and user-centric development, we’re poised to grow into a product that people truly want and trust. Security Security has been at the forefront of our design. We’ve implemented robust authentication protocols, encrypted data storage with Supabase, and strict privacy controls to ensure that every dream remains confidential. Our commitment to security isn’t an afterthought—it’s an integral part of the user experience, reflecting our dedication to building a trusted platform where users can safely explore their subconscious. Intuitive UX Our design philosophy is centered on clarity, accessibility, and engagement. By harnessing modern frameworks like React, Shadcn/UI, Tailwind CSS, and Framer Motion, we crafted an interface that is both visually appealing and remarkably intuitive. Every element—from the fluid animations to the responsive layouts—was designed to guide users effortlessly, ensuring that even the most complex features are easily accessible. We also have light mode AND dark mode!

## README (from the GitHub repository)

# Slumber Synthesizer - AI-Powered Dream Recording Platform

A modern web application for recording, analyzing, and sharing dreams with AI-enhanced features and community interaction.

Website: <https://lucidrem.com/>

## Features

### Dream Recording

- Record personal dreams with titles and descriptions
- Categorize dreams (nightmare, lucid, recurring, prophetic, normal)
- Add emotional context (joy, fear, confusion, anxiety, peace, etc.)
- AI-generated dream imagery
- AI-enhanced descriptions and interpretations

### Community Features

- Share dreams publicly
- Browse community dreams
- View AI-generated dream images
- Privacy controls for sharing

### Analytics

- Track dream patterns
- View emotional and category distributions
- Weekly dream frequency analysis
- Visual statistics and charts

## Tech Stack

- **Frontend**: React, TypeScript, Vite
- **UI**: Tailwind CSS, Shadcn/UI, Framer Motion
- **State Management**: TanStack Query
- **Backend**: Supabase (Auth, Database, Storage, Edge Functions)
- **AI Integration**: Google Gemini, OpenAI, Mistral AI

## Getting Started

### Prerequisites

- Node.js (v18 or higher)
- npm or yarn
- Supabase account
- OpenAI API key (for AI features)

### Installation

1. Clone the repository:

```bash
git clone <repository-url>
cd dream-journal
```

2. Install dependencies:

```bash
npm install
```

3. Create a `.env` file in the root directory and add your environment variables:

```env
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
OPENAI_API_KEY=your_openai_api_key
GEMINI_API_KEY=your_gemini_api_key
```

4. Start the development server:

```bash
npm run dev
```

The application will be available at `http://localhost:8080` or at the port specified by the `PORT` environment variable.

## Development

### Project Structure

```text
src/
├── components/     # Reusable UI components
├── contexts/       # React contexts
├── hooks/         # Custom hooks
├── lib/           # Utility functions and types
├── pages/         # Page components
└── integrations/  # Third-party service integrations
```

### Available Scripts

- `npm run dev` - Start development server
- `npm run build` - Build for production
- `npm run preview` - Preview production build
- `npm run lint` - Run ESLint
- `npm run typecheck` - Run TypeScript type checking

## Environment Variables and Security

### Managing Sensitive API Keys

This project uses environment variables to securely manage sensitive information such as API keys and tokens. Here's how to properly set up and manage your environment variables:

1. Copy the `.env.example` file to a new file named `.env`:

   ```bash
   cp .env.example .env
   ```

2. Fill in your actual API keys and credentials in the `.env` file. This file should **never** be committed to the repository.

3. Run the environment sync script to ensure all parts of the application have access to the required variables:
   ```bash
   ./scripts/sync_env.sh
   ```

### Security Best Practices

- **Never commit your `.env` file** to version control
- **Never hardcode sensitive information** in your source code
- Use session storage instead of localStorage for temporary client-side storage of sensitive data
- When deploying, set environment variables through your hosting platform's secure environment configuration
- Regularly rotate API keys and secrets

If you're developing new features that require additional environment variables:

1. Add them to your `.env` file
2. Add them to `.env.example` with placeholder values
3. Update the `sync_env.sh` script if they need to be available to Supabase Edge Functions

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Acknowledgments

- Supabase for backend services
- Google Gemini for AI image generation
- OpenAI for text generation
- Mistral AI for dream analysis
- Shadcn/UI for component library
- Framer Motion for animations


## Detected evidence (automated analysis)

Indexed codebase: 309 recognized source files, 1807 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (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
- Mistral AI (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 338)

```
.github/workflows/trunk-check.yaml
.gitignore
.trunk/.gitignore
.trunk/configs/.markdownlint.yaml
.trunk/configs/.shellcheckrc
.trunk/configs/.yamllint.yaml
.trunk/configs/svgo.config.mjs
.trunk/trunk.yaml
api/og/dream/[dreamId].tsx
api/og/user/[username].tsx
bun.lockb
components.json
dream-journal/.gitignore
dream-journal/app/globals.css
dream-journal/app/layout.tsx
dream-journal/app/page.tsx
dream-journal/components/dream-analysis.tsx
dream-journal/components/dream-card.tsx
dream-journal/components/dream-globe.tsx
dream-journal/components/dream-image-generator.tsx
dream-journal/components/dream-scene.tsx
dream-journal/components/dream-sharing.tsx
dream-journal/components/dream-statistics.tsx
dream-journal/components/feature-grid.tsx
dream-journal/components/floating-nav.tsx
dream-journal/components/particle-field.tsx
dream-journal/components/testimonial-slider.tsx
dream-journal/components/theme-provider.tsx
dream-journal/components/ui/accordion.tsx
dream-journal/components/ui/alert-dialog.tsx
dream-journal/components/ui/alert.tsx
dream-journal/components/ui/aspect-ratio.tsx
dream-journal/components/ui/avatar.tsx
dream-journal/components/ui/badge.tsx
dream-journal/components/ui/breadcrumb.tsx
dream-journal/components/ui/button.tsx
dream-journal/components/ui/calendar.tsx
dream-journal/components/ui/card.tsx
dream-journal/components/ui/carousel.tsx
dream-journal/components/ui/chart.tsx
dream-journal/components/ui/checkbox.tsx
dream-journal/components/ui/collapsible.tsx
dream-journal/components/ui/command.tsx
dream-journal/components/ui/context-menu.tsx
dream-journal/components/ui/dialog.tsx
dream-journal/components/ui/drawer.tsx
dream-journal/components/ui/dropdown-menu.tsx
dream-journal/components/ui/form.tsx
dream-journal/components/ui/hover-card.tsx
dream-journal/components/ui/input-otp.tsx
dream-journal/components/ui/input.tsx
dream-journal/components/ui/label.tsx
dream-journal/components/ui/menubar.tsx
dream-journal/components/ui/navigation-menu-styles.tsx
dream-journal/components/ui/navigation-menu.tsx
dream-journal/components/ui/pagination.tsx
dream-journal/components/ui/popover.tsx
dream-journal/components/ui/progress.tsx
dream-journal/components/ui/radio-group.tsx
dream-journal/components/ui/resizable.tsx
dream-journal/components/ui/scroll-area.tsx
dream-journal/components/ui/select.tsx
dream-journal/components/ui/separator.tsx
dream-journal/components/ui/sheet.tsx
dream-journal/components/ui/sidebar.tsx
dream-journal/components/ui/skeleton.tsx
dream-journal/components/ui/slider.tsx
dream-journal/components/ui/sonner.tsx
dream-journal/components/ui/switch.tsx
dream-journal/components/ui/table.tsx
dream-journal/components/ui/tabs.tsx
dream-journal/components/ui/textarea.tsx
dream-journal/components/ui/toast.tsx
dream-journal/components/ui/toaster.tsx
dream-journal/components/ui/toggle-group.tsx
dream-journal/components/ui/toggle.tsx
dream-journal/components/ui/tooltip.tsx
dream-journal/components/ui/use-form-field.tsx
dream-journal/components/ui/use-mobile.tsx
dream-journal/components/ui/use-sidebar.tsx
dream-journal/components/ui/use-toast.ts
dream-journal/hooks/use-mobile.tsx
dream-journal/hooks/use-toast.ts
dream-journal/lib/utils.ts
dream-journal/public/models/human-brain.glb
dream-journal/styles/globals.css
dream-journal/tailwind.config.ts
dream-journal/tsconfig.json
eslint.config.js
index.html
integrations/auth/provider.tsx
package.json
postcss.config.js
public/.well-known/apple-app-site-association
public/.well-known/assetlinks.json
public/favicon_io/site.webmanifest
public/manifest.json
public/models/human-brain.glb
public/robots.txt
public/sitemap.xml
README.md
schema.sql
src/App.css
src/App.tsx
src/components/audio/ApiKeyInput.tsx
src/components/audio/VoiceRecorder.tsx
src/components/auth/PasswordResetInstructions.tsx
src/components/auth/PasswordResetTest.tsx
src/components/auth/PasswordResetTester.tsx
src/components/dream-journal/app-preview.tsx
src/components/dream-journal/dream-analysis.tsx
src/components/dream-journal/dream-card.tsx
src/components/dream-journal/dream-globe.tsx
src/components/dream-journal/dream-image-generator.tsx
src/components/dream-journal/dream-scene.tsx
src/components/dream-journal/dream-sharing.tsx
src/components/dream-journal/dream-statistics.tsx
src/components/dream-journal/feature-grid.tsx
src/components/dream-journal/floating-nav.tsx
src/components/dream-journal/hero-app-preview.tsx
[218 more files omitted for size]
```

### Dependencies

- package.json: @eslint/js@^9.27.0, @hookform/resolvers@^3.9.0, @radix-ui/react-accordion@^1.2.0, @radix-ui/react-alert-dialog@^1.1.1, @radix-ui/react-aspect-ratio@^1.1.0, @radix-ui/react-avatar@^1.1.0, @radix-ui/react-checkbox@^1.1.1, @radix-ui/react-collapsible@^1.1.0, @radix-ui/react-context-menu@^2.2.1, @radix-ui/react-dialog@^1.1.2, @radix-ui/react-dropdown-menu@^2.1.1, @radix-ui/react-hover-card@^1.1.1, @radix-ui/react-label@^2.1.0, @radix-ui/react-menubar@^1.1.1, @radix-ui/react-navigation-menu@^1.2.0, @radix-ui/react-popover@^1.1.1, @radix-ui/react-progress@^1.1.0, @radix-ui/react-radio-group@^1.2.0, @radix-ui/react-scroll-area@^1.1.0, @radix-ui/react-select@^2.1.1, @radix-ui/react-separator@^1.1.0, @radix-ui/react-slider@^1.2.0, @radix-ui/react-slot@^1.1.0, @radix-ui/react-switch@^1.1.0, @radix-ui/react-tabs@^1.1.0, @radix-ui/react-toast@^1.2.1, @radix-ui/react-toggle@^1.1.0, @radix-ui/react-toggle-group@^1.1.0, @radix-ui/react-tooltip@^1.1.4, @react-three/drei@^9.122.0, @react-three/fiber@^8.18.0, @react-three/postprocessing@^2.19.1, @supabase/supabase-js@^2.49.4, @tailwindcss/typography@^0.5.15, @tanstack/react-query@^5.56.2, @types/node@^22.5.5, @types/react@^18.3.3, @types/react-dom@^18.3.0, @types/react-helmet-async@^1.0.3, @vercel/analytics@^1.5.0, @vercel/og@^0.6.8, @vercel/speed-insights@^1.2.0, @vitejs/plugin-react-swc@^3.5.0, autoprefixer@^10.4.20, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.0.0, date-fns@^3.6.0, embla-carousel-react@^8.3.0, eslint@^9.9.0, eslint-plugin-react-hooks@^5.1.0-rc.0, eslint-plugin-react-refresh@^0.4.9, framer-motion@^12.4.3, globals@^15.9.0, input-otp@^1.2.4, lamejs@^1.2.1, lovable-tagger@^1.1.3, lucide-react@^0.462.0, next-themes@^0.3.0, postcss@^8.4.47, react@^18.3.1, react-day-picker@^8.10.1, react-dom@^18.3.1, react-helmet-async@^2.0.5, react-hook-form@^7.53.0, react-resizable-panels@^2.1.3, react-router-dom@^6.26.2, recharts@^2.12.7, sonner@^1.5.0, supabase@^2.20.12, tailwind-merge@^2.5.2, tailwindcss@^3.4.17, tailwindcss-animate@^1.0.7, three@^0.159.0, typescript@^5.5.3, typescript-eslint@^8.0.1, vaul@^0.9.3, vite@^5.4.1, zod@^3.23.8

### Recent commits (newest first)

- Merge pull request #109 from MiPlayer123/mikulsaravanan/rem-189-updated-landing-page
- fixed
- Merge pull request #108 from MiPlayer123/mikulsaravanan/rem-189-updated-landing-page
- images
- Merge pull request #106 from MiPlayer123/mikulsaravanan/rem-189-updated-landing-page
- Merge branch 'main' into mikulsaravanan/rem-189-updated-landing-page
- Merge pull request #107 from MiPlayer123/privacy-policy
- privacy
- temp google play disable
- profile redirect
- apple url
- trunk
- init commit for new landing
- fina test
- test
- debug ogs
- Update favicon paths in site.webmanifest
- Merge pull request #104 from MiPlayer123/mikulsaravanan/rem-188-og-images
- Merge branch 'main' into mikulsaravanan/rem-188-og-images
- Merge branch 'mikulsaravanan/rem-188-og-images' of https://github.com/MiPlayer123/slumber-synthesizer into mikulsaravanan/rem-188-og-images

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

### package.json

```
{
  "name": "vite_react_shadcn_ts",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "build:dev": "vite build --mode development",
    "lint": "eslint .",
    "preview": "vite preview",
    "fix-subscription-status": "node fix_subscription_status.js"
  },
  "dependencies": {
    "@hookform/resolvers": "^3.9.0",
    "@radix-ui/react-accordion": "^1.2.0",
    "@radix-ui/react-alert-dialog": "^1.1.1",
    "@radix-ui/react-aspect-ratio": "^1.1.0",
    "@radix-ui/react-avatar": "^1.1.0",
    "@radix-ui/react-checkbox": "^1.1.1",
    "@radix-ui/react-collapsible": "^1.1.0",
    "@radix-ui/react-context-menu": "^2.2.1",
    "@radix-ui/react-dialog": "^1.1.2",
    "@radix-ui/react-dropdown-menu": "^2.1.1",
    "@radix-ui/react-hover-card": "^1.1.1",
    "@radix-ui/react-label": "^2.1.0",
    "@radix-ui/react-menubar": "^1.1.1",
    "@radix-ui/react-navigation-menu": "^1.2.0",
    "@radix-ui/react-popover": "^1.1.1",
    "@radix-ui/react-progress": "^1.1.0",
    "@radix-ui/react-radio-group": "^1.2.0",
    "@radix-ui/react-scroll-area": "^1.1.0",
    "@radix-ui/react-select": "^2.1.1",
    "@radix-ui/react-separator": "^1.1.0",
    "@radix-ui/react-slider": "^1.2.0",
    "@radix-ui/react-slot": "^1.1.0",
    "@radix-ui/react-switch": "^1.1.0",
    "@radix-ui/react-tabs": "^1.1.0",
    "@radix-ui/react-toast": "^1.2.1",
    "@radix-ui/react-toggle": "^1.1.0",
    "@radix-ui/react-toggle-group": "^1.1.0",
    "@radix-ui/react-tooltip": "^1.1.4",
    "@react-three/drei": "^9.122.0",
    "@react-three/fiber": "^8.18.0",
    "@react-three/postprocessing": "^2.19.1",
    "@supabase/supabase-js": "^2.49.4",
    "@tanstack/react-query": "^5.56.2",
    "@vercel/analytics": "^1.5.0",
    "@vercel/og": "^0.6.8",
    "@vercel/speed-insights": "^1.2.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.0.0",
    "date-fns": "^3.6.0",
    "embla-carousel-react": "^8.3.0",
    "framer-motion": "^12.4.3",
    "input-otp": "^1.2.4",
    "lamejs": "^1.2.1",
    "lucide-react": "^0.462.0",
    "next-themes": "^0.3.0",
    "react": "^18.3.1",
    "react-day-picker": "^8.10.1",
    "react-dom": "^18.3.1",
    "react-helmet-async": "^2.0.5",
    "react-hook-form": "^7.53.0",
    "react-resizable-panels": "^2.1.3",
    "react-router-dom": "^6.26.2",
    "recharts": "^2.12.7",
    "sonner": "^1.5.0",
    "tailwind-merge": "^2.5.2",
    "tailwindcss-animate": "^1.0.7",
    "three": "^0.159.0",
    "vaul": "^0.9.3",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@eslint/js": "^9.27.0",
    "@tailwindcss/typography": "^0.5.15",
    "@types/node": "^22.5.5",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@types/react-helmet-async": "^1.0.3",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "autoprefixer": "^10.4.20",
    "eslint": "^9.9.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.9",
    "globals": "^15.9.0",
    "lovable-tagger": "^1.1.3",
    "postcss": "^8.4.47",
    "supabase": "^2.20.12",
    "tailwindcss": "^3.4.17",
    "typescript": "^5.5.3",
    "typescript-eslint": "^8.0.1",
    "vite": "^5.4.1"
  },
  "overrides": {
    "esbuild": "^0.25.4"
  }
}

```

### src/main.tsx

```typescript
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { initializeApp } from "./services/appInit";

// Initialize application services before rendering
initializeApp();

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

```

### src/App.tsx

```typescript
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate,
  useLocation,
  useNavigate,
} from "react-router-dom";
import { ThemeProvider } from "@/hooks/use-theme";
import { Toaster } from "@/components/ui/toaster";
import { Navigation } from "@/components/Navigation";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AuthProvider } from "@/contexts/AuthContext";
import { useAuth } from "@/hooks/useAuth";
import { Suspense, lazy, useEffect } from "react";
import { SpeedInsights } from "@vercel/speed-insights/react";
import { Analytics } from "@vercel/analytics/react";
import { HelmetProvider } from "react-helmet-async";

// Pages
import Index from "@/pages/Index";
import Auth from "@/pages/Auth";
import NotFound from "@/pages/NotFound";
import ResetPassword from "@/pages/ResetPassword";
import PasswordResetTroubleshoot from "@/pages/PasswordResetTroubleshoot";
import PasswordResetDebug from "@/pages/PasswordResetDebug";
import About from "@/pages/About";
import Privacy from "@/pages/Privacy";
import Terms from "@/pages/Terms";
import { CheckoutComplete } from "@/components/subscription/CheckoutComplete";
import { CheckoutRedirect } from "@/components/subscription/CheckoutRedirect";

// Lazy loaded pages for performance
const Journal = lazy(() => import("@/pages/Journal"));
const Community = lazy(() => import("@/pages/Community"));
const Statistics = lazy(() => import("@/pages/Statistics"));
const DreamWall = lazy(() => import("@/pages/DreamWall"));
const Settings = lazy(() => import("@/pages/Settings"));
const FriendsFeed = lazy(() => import("@/pages/FriendsFeed"));
const ManageFriendsPage = lazy(() => import("@/pages/ManageFriendsPage"));

// Direct imports for public routes and their /app counterparts
import DreamDetail from "@/pages/DreamDetail";
import { UserProfile } from "@/pages/UserProfile";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 1,
      refetchOnWindowFocus: false,
      refetchOnMount: true,
      staleTime: 1000 * 30, // 30 seconds
    },
  },
});

// Loading component
const LoadingSpinner = () => (
  <div className="min-h-screen flex items-center justify-center">
    <div className="flex flex-col items-center">
      <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-dream-600 mb-4"></div>
      <p className="text-dream-600">Loading...</p>
    </div>
  </div>
);

// Protected route component with better loading handling
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
  const { user, loading } = useAuth();
  const location = useLocation();

  // Show loading spinner only during initial authentication check
  if (loading) {
    return <LoadingSpinner />;
  }

  // If not authenticated, redirect to auth page and remember the intended destination
  if (!user) {
    return <Navigate to="/auth" state={{ from: location }} replace />;
  }

  // Render children wrapped in Suspense for lazy loading
  return <Suspense fallback={<LoadingSpinner />}>{children}</Suspense>;
};

// Auth handler component to process tokens at root path
const AuthRedirectHandler = () => {
  const location = useLocation();
  const navigate = useNavigate();
  const { user, loading, needsProfileCompletion } = useAuth();

  useEffect(() => {
    // Only run on mount
    const searchParams = new URLSearchParams(location.search);

    console.log("Root path: Checking URL parameters", {
      search: location.search,
      hash: location.hash,
      hasCode: searchParams.has("code"),
      needsProfileCompletion,
      hasUser: !!user,
    });

    // Check if this is a Google sign-in callback
    if (
      searchParams.has("code") &&
      searchParams.has("signin") &&
      searchParams.get("signin") === "google"
    ) {
      console.log("Detected Google sign-in callback, redirecting to auth page");
      navigate("/auth", { replace: true });
      return;
    }

    // Check for explicit password reset flow markers
    const isPasswordReset =
      location.hash.includes("type=recovery") ||
      searchParams.get("type") === "recovery";

    // Check for Supabase auth code (used in the password reset flow)
    if (searchParams.has("code") && isPasswordReset) {
      console.log(
        "Detected Supabase auth code for password reset, redirecting to reset-password with code",
        {
          code: searchParams.get("code"),
        },
      );

      // Redirect to the reset password page with the code parameter
      navigate(`/reset-password${location.search}${location.hash}`, {
        replace: true,
      });
      return;
    }

    // Check for auth-related parameters
    if (
      searchParams.has("error") ||
      searchParams.has("access_token") ||
      searchParams.has("refresh_token") ||
      searchParams.has("token") ||
      searchParams.has("type")
    ) {
      // Check for recovery specifically
      if (
        searchParams.get("type") === "recovery" ||
        location.hash.includes("type=recovery")
      ) {
        console.log("Detected recovery flow, redirecting to reset-password", {
          search: location.search,
          hash: location.hash,
        });

        // Redirect to the reset password page with all query params and hash intact
        navigate(`/reset-password${location.search}${location.hash}`, {
          replace: true,
        });
        return;
      }

      // Check for error related to token expiration
      if (
        searchParams.get("error") === "access_denied" &&
        searchParams.get("error_code") === "otp_expired"
      ) {
        console.log("Detected expired token");
        navigate("/password-reset-troubleshoot?expired=true", {
          replace: true,
        });
        return;
      }
    }

    // If user is logged in but needs profile completion, redirect to auth page
    if (user && !loading && needsProfileCompletion) {
      console.log("User needs profile completion, redirecting to auth pag
[truncated — 5122 more characters]
```

### dream-journal/app/layout.tsx

```typescript
import type React from "react";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";

// eslint-disable-next-line react-refresh/only-export-components
export const metadata = {
  title: "Rem - Dream Journal & Analysis",
  description:
    "Record your dreams, uncover hidden patterns, and connect with a community of dreamers.",
  generator: "v0.dev",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link
          rel="preconnect"
          href="https://fonts.gstatic.com"
          crossOrigin="anonymous"
        />
        <link
          href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
          rel="stylesheet"
        />
      </head>
      <body className="font-sans">
        <ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

```

### dream-journal/app/page.tsx

```typescript
"use client";

import { useRef, useState, useEffect } from "react";
import { ArrowRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { motion, AnimatePresence } from "framer-motion";
import DreamScene from "@/components/dream-scene";
import { DreamCard } from "@/components/dream-card";
import { FeatureGrid } from "@/components/feature-grid";
import { TestimonialSlider } from "@/components/testimonial-slider";
import { FloatingNav } from "@/components/floating-nav";
import { ParticleField } from "@/components/particle-field";
import { DreamGlobe } from "@/components/dream-globe";
import { DreamStatistics } from "@/components/dream-statistics";
import { DreamImageGenerator } from "@/components/dream-image-generator";
import { DreamSharing } from "@/components/dream-sharing";
import { DreamAnalysis } from "@/components/dream-analysis";
import { Brain, BarChart, Sparkles, Share2 } from "lucide-react";

// Define the interface for dream items
interface DreamItem {
  id: number;
  title: string;
  color: string;
}

export default function Home() {
  const [scrollY, setScrollY] = useState(0);
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
  const heroRef = useRef<HTMLDivElement>(null);

  const sampleDreamData = [
    {
      id: 1,
      title: "Flying over mountains",
      color: "#8b5cf6",
    },
    {
      id: 2,
      title: "Lost in a maze",
      color: "#3b82f6",
    },
    {
      id: 3,
      title: "Underwater city",
      color: "#06b6d4",
    },
    {
      id: 4,
      title: "Chased by shadows",
      color: "#10b981",
    },
    {
      id: 5,
      title: "Floating in space",
      color: "#8b5cf6",
    },
  ];

  const [selectedDream, setSelectedDream] = useState<DreamItem | null>(null);

  useEffect(() => {
    if (typeof window !== "undefined") {
      const handleScroll = () => {
        setScrollY(window.scrollY);
      };

      window.addEventListener("scroll", handleScroll, { passive: true });
      return () => window.removeEventListener("scroll", handleScroll);
    }
  }, []);

  return (
    <div className="min-h-screen bg-background text-white overflow-x-hidden">
      {/* Particle Background */}
      <div className="fixed inset-0 z-0 opacity-30">
        <ParticleField />
      </div>

      {/* Header */}
      <FloatingNav />

      {/* Mobile Menu */}
      <AnimatePresence>
        {mobileMenuOpen && (
          <motion.div
            initial={{ opacity: 0, x: "100%" }}
            animate={{ opacity: 1, x: 0 }}
            exit={{ opacity: 0, x: "100%" }}
            transition={{ type: "spring", damping: 30 }}
            className="fixed inset-0 z-50 bg-black/95 backdrop-blur-md md:hidden"
          >
            <div className="flex flex-col h-full p-6">
              <div className="flex justify-between items-center mb-10">
                <div className="flex items-center gap-2">
                  <img
                    src="/apple-touch-icon.png"
                    alt="REM Logo"
                    className="h-8 w-8"
                  />
                  <span className="text-lg font-medium tracking-tight">
                    Rem
                  </span>
                </div>
                <Button
                  variant="ghost"
                  size="icon"
                  onClick={() => setMobileMenuOpen(false)}
                  className="text-white"
                >
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    width="24"
                    height="24"
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  >
                    <line x1="18" y1="6" x2="6" y2="18"></line>
                    <line x1="6" y1="6" x2="18" y2="18"></line>
                  </svg>
                </Button>
              </div>
              <nav className="flex flex-col gap-6 text-lg">
                <a
                  href="#features"
                  className="py-2 hover:text-purple-400 transition-colors"
                  onClick={() => setMobileMenuOpen(false)}
                >
                  Features
                </a>
                <a
                  href="#testimonials"
                  className="py-2 hover:text-purple-400 transition-colors"
                  onClick={() => setMobileMenuOpen(false)}
                >
                  Testimonials
                </a>
                <a
                  href="#"
                  className="py-2 hover:text-purple-400 transition-colors"
                  onClick={() => setMobileMenuOpen(false)}
                >
                  About
                </a>
                <a
                  href="#"
                  className="py-2 hover:text-purple-400 transition-colors"
                  onClick={() => setMobileMenuOpen(false)}
                >
                  Blog
                </a>
              </nav>
              <div className="mt-auto space-y-4">
                <Button
                  variant="ghost"
                  className="w-full text-white hover:bg-white/10"
                  onClick={() => setMobileMenuOpen(false)}
                >
                  Log in
                </Button>
                <Button
                  className="w-full bg-white text-black hover:bg-white/90"
                  onClick={() => setMobileMenuOpen(false)}
                >
                  Sign up
                </Button>
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      <main className="relative z-10">
        {/* Hero Section */}
        <section
          ref={heroRef}
          className="relative min-h-screen flex items-center justify-center overflow-hidden"
        >
          <div className="absolute inset-0 
[truncated — 15763 more characters]
```

### supabase/functions/backfill-dream-images/index.ts

```typescript
// backfill-dream-images-once/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.7.1";

serve(async () => {
  const url = Deno.env.get("SUPABASE_URL")!;
  const key = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
  const supabase = createClient(url, key);

  // 1) fetch all dreams AFTER April 20, 2025 missing an image
  const { data: dreams, error: fetchError } = await supabase
    .from("dreams")
    .select("id, title, description")
    .is("image_url", null)
    .is("enhanced_description", null)
    .gt("created_at", "2025-04-20T00:00:00Z");

  if (fetchError) {
    console.error("Failed to fetch dreams:", fetchError);
    return new Response(fetchError.message, { status: 500 });
  }
  console.log(`Found ${dreams.length} dreams to backfill`);

  // 2) invoke your edge function for each, passing both id and description
  for (const d of dreams) {
    const payload = {
      dreamId: d.id,
      description: `${d.title} – ${d.description}`,
    };

    console.log("Invoking generate-dream-image for:", d.id);
    const { data, error } = await supabase.functions.invoke(
      "generate-dream-image",
      { body: payload, headers: { Authorization: `Bearer ${key}` } },
    );

    if (error) {
      console.error(`Invocation error for ${d.id}:`, error);
    } else {
      console.log(`Invocation response for ${d.id}:`, data);
    }
  }

  return new Response(JSON.stringify({ backfilled: dreams.length }), {
    headers: { "Content-Type": "application/json" },
  });
});

```

### src/pages/Index.tsx

```typescript
import { useRef, useState, useEffect } from "react";
import { ArrowRight, Download } from "lucide-react";
import { Button } from "@/components/ui/button";
import { motion } from "framer-motion";
// import DreamScene from "@/components/dream-journal/dream-scene";
import { FeatureGrid } from "@/components/dream-journal/feature-grid";
import { TestimonialSlider } from "@/components/dream-journal/testimonial-slider";
import { ParticleField } from "@/components/dream-journal/particle-field";
import { DreamStatistics } from "@/components/dream-journal/dream-statistics";
import { DreamImageGenerator } from "@/components/dream-journal/dream-image-generator";
import { DreamSharing } from "@/components/dream-journal/dream-sharing";
import { DreamAnalysis } from "@/components/dream-journal/dream-analysis";
import { MobileAppPreview } from "@/components/dream-journal/mobile-app-preview";
import { AppStoreBadges } from "@/components/ui/app-store-badges";
import { Brain, BarChart, Sparkles, Share2 } from "lucide-react";
import { Navigation } from "@/components/Navigation";
import { useAuth } from "@/hooks/useAuth";
import { useNavigate } from "react-router-dom";
import {
  getDeviceType,
  getMobileDownloadUrl,
  type DeviceType,
} from "@/utils/device-detection";

export default function Index() {
  const heroRef = useRef<HTMLDivElement>(null);
  const { loading } = useAuth();
  const navigate = useNavigate();
  const [deviceType, setDeviceType] = useState<DeviceType>("web");

  // Detect device type on mount
  useEffect(() => {
    setDeviceType(getDeviceType());
  }, []);

  // Handle "Download Now" - smart routing based on device
  const handleDownloadNow = () => {
    if (deviceType === "web") {
      // Store sign-up preference in sessionStorage so Auth page knows to show sign-up form
      sessionStorage.setItem("auth-mode", "signup");
      navigate("/auth");
    } else {
      // Redirect to appropriate app store
      const downloadUrl = getMobileDownloadUrl(deviceType);
      window.open(downloadUrl, "_blank");
    }
  };

  // Handle "Sign Up Free" - navigate to auth page in sign-up mode
  const handleSignUpFree = () => {
    // Store sign-up preference in sessionStorage so Auth page knows to show sign-up form
    sessionStorage.setItem("auth-mode", "signup");
    navigate("/auth");
  };

  return (
    <div className="min-h-screen bg-[#1a0b2e] text-white overflow-x-hidden">
      {/* Particle Background */}
      <div className="fixed inset-0 z-0 opacity-30">
        <ParticleField />
      </div>

      {/* Header */}
      <Navigation />

      {/* Navigation renders mobile menu automatically */}

      <main className="relative z-10">
        {/* Hero Section */}
        <section
          ref={heroRef}
          className="relative min-h-[90vh] flex items-center justify-center overflow-hidden"
        >
          <div className="absolute inset-0 z-0">
            {/* Temporarily disabled DreamScene */}
            {/* <DreamScene scrollY={scrollY} /> */}
            <div className="w-full h-full bg-gradient-to-br from-purple-900/20 to-blue-900/20" />
          </div>

          <div className="container mx-auto px-6 py-16 md:py-24 relative z-10">
            <div className="grid lg:grid-cols-2 gap-12 items-center min-h-[70vh]">
              {/* Left side - Value proposition and CTAs */}
              <motion.div
                initial={{ opacity: 0, y: 20 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ duration: 0.8, delay: 0.2 }}
                className="space-y-8 text-center lg:text-left"
              >
                <h1 className="text-5xl md:text-6xl lg:text-7xl font-extrabold tracking-tight leading-tight bg-clip-text text-transparent bg-gradient-to-r from-purple-300 via-blue-400 to-cyan-400 drop-shadow-lg">
                  Track Your Dreams,
                  <br />
                  Transform Your Mind
                </h1>
                <p className="text-base md:text-lg text-white/80 leading-relaxed max-w-2xl mx-auto lg:mx-0">
                  Join hundreds using AI-powered dream analysis to unlock
                  insights into their subconscious mind and improve mental
                  wellness.
                </p>

                {/* Social Proof */}
                <motion.div
                  initial={{ opacity: 0, y: 10 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ duration: 0.6, delay: 0.3 }}
                  className="flex items-center justify-center lg:justify-start gap-4 text-xs text-white/60"
                >
                  <div className="flex items-center gap-1.5">
                    <div className="w-1.5 h-1.5 bg-green-400 rounded-full"></div>
                    <span>Free to start</span>
                  </div>
                  <div className="flex items-center gap-1.5">
                    <div className="w-1.5 h-1.5 bg-blue-400 rounded-full"></div>
                    <span>Explore the community</span>
                  </div>
                  <div className="flex items-center gap-1.5">
                    <div className="w-1.5 h-1.5 bg-purple-400 rounded-full"></div>
                    <span>AI insights</span>
                  </div>
                </motion.div>

                {/* Bold CTAs */}
                <motion.div
                  initial={{ opacity: 0, y: 20 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ duration: 0.8, delay: 0.4 }}
                  className="flex flex-col justify-center lg:justify-start gap-4"
                >
                  {/* CTA Buttons Row */}
                  <div className="flex flex-col sm:flex-row justify-center lg:justify-start gap-4">
                    {/* Primary CTA - Download Now */}
                    <Button
                      onClick={handleDownloadNow}
                      disabled={loading}
                      size="lg"
                      className="bg-grad
[truncated — 17063 more characters]
```

### supabase/functions/subscription-cron/index.ts

```typescript
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.23.0";

// Get environment variables
const supabaseUrl = Deno.env.get("SUPABASE_URL") || "";
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || "";

// Create Supabase client
const supabase = createClient(supabaseUrl, supabaseServiceKey);

// Function to log debug messages
const logDebug = (message: string, data?: any) => {
  console.log(
    `[subscription-cron] ${message}`,
    data ? JSON.stringify(data) : "",
  );
};

/**
 * Scheduled function to process subscription expirations.
 * This function checks for subscriptions that:
 * 1. Are in "canceling" status
 * 2. Have reached their current_period_end date
 *
 * And updates them to "canceled" status.
 */
serve(async () => {
  try {
    const now = new Date();
    logDebug(`Running scheduled subscription check at ${now.toISOString()}`);

    // Get all subscriptions that are in "canceling" state and have period_end in the past
    const { data: expiredSubscriptions, error: queryError } = await supabase
      .from("customer_subscriptions")
      .select(
        "id, user_id, subscription_id, subscription_status, current_period_end, cancel_at_period_end",
      )
      .or(
        `subscription_status.eq.canceling,and(subscription_status.eq.active,cancel_at_period_end.eq.true)`,
      )
      .lt("current_period_end", now.toISOString());

    if (queryError) {
      logDebug("Error querying expired subscriptions", queryError);
      return new Response(
        JSON.stringify({ error: "Failed to query expired subscriptions" }),
        { status: 500, headers: { "Content-Type": "application/json" } },
      );
    }

    logDebug(
      `Found ${expiredSubscriptions.length} expired subscriptions to process`,
    );

    // Process each expired subscription
    const results = await Promise.all(
      expiredSubscriptions.map(async (subscription) => {
        logDebug(
          `Processing expired subscription for user ${subscription.user_id}`,
        );

        // Update the subscription status to "canceled"
        const { error: updateError } = await supabase
          .from("customer_subscriptions")
          .update({
            status: "canceled",
            subscription_status: "canceled",
            subscription_id: null,
            canceled_at: now.toISOString(),
            updated_at: now.toISOString(),
            cancel_at_period_end: false,
          })
          .eq("id", subscription.id);

        if (updateError) {
          logDebug(
            `Error updating subscription ${subscription.id}`,
            updateError,
          );
          return { id: subscription.id, success: false, error: updateError };
        } else {
          logDebug(
            `Successfully transitioned subscription ${subscription.id} to canceled status`,
          );
          return { id: subscription.id, success: true };
        }
      }),
    );

    // Return the results
    return new Response(
      JSON.stringify({
        processed: expiredSubscriptions.length,
        successes: results.filter((r) => r.success).length,
        failures: results.filter((r) => !r.success).length,
        timestamp: now.toISOString(),
      }),
      { status: 200, headers: { "Content-Type": "application/json" } },
    );
  } catch (error) {
    logDebug("Unhandled error in subscription cron", error);
    return new Response(
      JSON.stringify({
        error: "Internal server error",
        details: error.message,
      }),
      { status: 500, headers: { "Content-Type": "application/json" } },
    );
  }
});

```

### supabase/functions/transcribe-audio/index.ts

```typescript
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { decode as base64Decode } from "https://deno.land/std@0.208.0/encoding/base64.ts";
import { parseOpenAIError, createErrorResponse } from "../_shared/errors.ts";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers":
    "authorization, x-client-info, apikey, content-type, X-Client-Info",
  "Access-Control-Allow-Methods": "POST, OPTIONS",
};

serve(async (req) => {
  // Handle CORS preflight requests
  if (req.method === "OPTIONS") {
    return new Response(null, {
      status: 204, // Use 204 No Content for OPTIONS
      headers: corsHeaders,
    });
  }

  try {
    // Check if request is POST
    if (req.method !== "POST") {
      throw new Error(`Method ${req.method} not allowed`);
    }

    // Get the body data
    let body;
    try {
      body = await req.json();
    } catch (error) {
      console.error("Error parsing request body:", error);
      throw new Error("Invalid request body format");
    }

    const {
      audioBase64,
      fileExtension = "webm", // Default fallback
      mimeType = "audio/webm", // Default fallback
      userAgent = "Unknown", // Capture user agent if sent
    } = body;

    if (!audioBase64) {
      console.error("No audio data provided in request");
      throw new Error("No audio data provided");
    }

    console.log(`Received request: User-Agent: ${userAgent}`);
    console.log(
      `Received parameters: fileExtension=${fileExtension}, mimeType=${mimeType}`,
    );

    // Validate and normalize the file extension and mime type
    const normalizedExtension = fileExtension.replace(/^audio\//, "");
    let normalizedMimeType = mimeType;

    if (!normalizedMimeType.startsWith("audio/")) {
      normalizedMimeType = `audio/${normalizedExtension}`;
    }

    console.log(
      `Normalized parameters: extension=${normalizedExtension}, mimeType=${normalizedMimeType}`,
    );

    // Check if OPENAI_API_KEY is available
    const openaiApiKey = Deno.env.get("OPENAI_API_KEY");
    if (!openaiApiKey) {
      console.error("OpenAI API key is not set");
      throw new Error("OpenAI API key is not configured");
    }

    // Convert base64 audio to binary
    let audioBlob;
    try {
      audioBlob = base64Decode(audioBase64);
      console.log("Decoded audio data, size:", audioBlob.length, "bytes");
    } catch (error) {
      console.error("Error decoding base64 audio:", error);
      throw new Error("Invalid audio data format");
    }

    // Create a FormData object to send to OpenAI
    const formData = new FormData();
    const fileName = `recording.${normalizedExtension}`; // Use the determined extension

    try {
      // Create a file object from the binary data
      console.log(
        `Attempting to create File object: name=${fileName}, type=${normalizedMimeType}`,
      );
      const file = new File([audioBlob], fileName, {
        type: normalizedMimeType,
      });

      formData.append("file", file);
      formData.append("model", "whisper-1");
      formData.append("language", "en");

      console.log("FormData created successfully with File object");
    } catch (fileError) {
      console.error("Error creating File object:", fileError);

      // Fallback to Blob if File constructor fails
      try {
        console.log(
          `Attempting to create Blob object: name=${fileName}, type=${normalizedMimeType}`,
        );
        const blob = new Blob([audioBlob], { type: normalizedMimeType });
        formData.append("file", blob, fileName);
        formData.append("model", "whisper-1");
        formData.append("language", "en");

        console.log("FormData created successfully with Blob fallback");
      } catch (blobError) {
        console.error("Error creating Blob:", blobError);
        throw new Error(`Cannot create form data: ${blobError.message}`);
      }
    }

    console.log("Sending request to OpenAI API with file:", fileName);

    // Send request to OpenAI API with enhanced error handling
    let response;
    try {
      response = await fetch("https://api.openai.com/v1/audio/transcriptions", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${openaiApiKey}`,
        },
        body: formData,
      });

      console.log(
        "Received response from OpenAI API, status:",
        response.status,
      );
    } catch (error) {
      console.error("Network error when calling OpenAI API:", error);
      const errorResponse = parseOpenAIError(`Network error: ${error.message}`);
      return createErrorResponse(errorResponse);
    }

    if (!response.ok) {
      try {
        const errorText = await response.text();
        console.error("OpenAI API error response:", errorText);

        // Use the new error handler for OpenAI errors
        const errorResponse = parseOpenAIError(errorText);

        // Return a proper error response using our utility
        return createErrorResponse(errorResponse);
      } catch (e) {
        console.error("Error parsing error response:", e);
        const errorResponse = parseOpenAIError(`Status ${response.status}`);
        return createErrorResponse(errorResponse);
      }
    }

    let data;
    try {
      data = await response.json();
    } catch (error) {
      console.error("Error parsing OpenAI response:", error);
      throw new Error("Invalid response from OpenAI API");
    }

    if (!data.text) {
      console.error("No transcription in response:", data);
      throw new Error("No transcription returned from OpenAI");
    }

    console.log("Transcription completed successfully");

    return new Response(JSON.stringify({ text: data.text }), {
      status: 200,
      headers: { ...corsHeaders, "Content-Type": "application/json" },
    });
  } catch (error) {
    console.error("Function error:", error);

    // Using the new error handler for all types of errors
    const errorMessage = error instanceof Error
[truncated — 147 more characters]
```

### postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

```

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