From b1feda521c87764ae50cbb84cc830df323b3f654 Mon Sep 17 00:00:00 2001 From: Nicholai Date: Tue, 18 Nov 2025 13:37:20 -0700 Subject: [PATCH] Update configuration and middleware for Cloudflare integration; add Space Grotesk font and improve media route handling - Changed wrapper settings in open-next.config.ts to use "cloudflare-node" and "cloudflare-edge". - Updated main entry point in wrangler.toml to point to ".open-next/worker.js". - Modified middleware to allow access to the speakers project path. - Added Space Grotesk font to layout.tsx for enhanced typography. - Improved media route handling by resolving parameters correctly in route.ts. - Adjusted ServiceCard component to use a more specific type for icon handling. --- .claude/design-system-architect.md | 0 .cursorindexingignore | 3 + .specstory/.gitignore | 4 + ...2025-10-23_11-06Z-generate-cursor-rules.md | 1526 +++++++++++++++++ design.json | 272 +++ open-next.config.ts | 4 +- src/app/api/media/[...path]/route.ts | 9 +- src/app/layout.tsx | 12 +- src/app/projects/speakers/page.tsx | 19 + src/app/projects/speakers/speakers-client.tsx | 164 ++ src/components/PolycamEmbed.tsx | 172 ++ src/components/ServiceCard.tsx | 2 +- src/data/speakers.ts | 15 + src/middleware.ts | 4 +- wrangler.toml | 2 +- 15 files changed, 2196 insertions(+), 12 deletions(-) create mode 100644 .claude/design-system-architect.md create mode 100644 .cursorindexingignore create mode 100644 .specstory/.gitignore create mode 100644 .specstory/history/2025-10-23_11-06Z-generate-cursor-rules.md create mode 100644 design.json create mode 100644 src/app/projects/speakers/page.tsx create mode 100644 src/app/projects/speakers/speakers-client.tsx create mode 100644 src/components/PolycamEmbed.tsx create mode 100644 src/data/speakers.ts diff --git a/.claude/design-system-architect.md b/.claude/design-system-architect.md new file mode 100644 index 0000000..e69de29 diff --git a/.cursorindexingignore b/.cursorindexingignore new file mode 100644 index 0000000..953908e --- /dev/null +++ b/.cursorindexingignore @@ -0,0 +1,3 @@ + +# Don't index SpecStory auto-save files, but allow explicit context inclusion via @ references +.specstory/** diff --git a/.specstory/.gitignore b/.specstory/.gitignore new file mode 100644 index 0000000..c5b4129 --- /dev/null +++ b/.specstory/.gitignore @@ -0,0 +1,4 @@ +# SpecStory project identity file +/.project.json +# SpecStory explanation file +/.what-is-this.md diff --git a/.specstory/history/2025-10-23_11-06Z-generate-cursor-rules.md b/.specstory/history/2025-10-23_11-06Z-generate-cursor-rules.md new file mode 100644 index 0000000..2c89981 --- /dev/null +++ b/.specstory/history/2025-10-23_11-06Z-generate-cursor-rules.md @@ -0,0 +1,1526 @@ + + + + +# Generate cursor rules (2025-10-23 11:06Z) + +_**User (2025-10-23 11:06Z)**_ + +/Generate Cursor Rules + +--- + +_**Agent (model default, mode Agent)**_ + +I'll analyze the codebase and generate comprehensive Cursor rules based on the project structure and the existing AGENTS.md guide. Let me first examine the current project structure and then create appropriate rules. + + +--- + + +
Tool use: **read_file** • Read file: /home/nicholai/Documents/dev/biohazard-vfx-website/AGENTS.md + +
+
+ +--- + + +
Tool use: **read_file** • Read file: /home/nicholai/Documents/dev/biohazard-vfx-website/package.json + +
+
+ +--- + + +
Tool use: **read_file** • Read file: /home/nicholai/Documents/dev/biohazard-vfx-website/next.config.ts + +
+
+ +--- + + +
Tool use: **read_file** • Read file: /home/nicholai/Documents/dev/biohazard-vfx-website/tsconfig.json + +
+
+ +--- + +Now let me check the middleware and some key components to understand the project better: + + +--- + + +
Tool use: **read_file** • Read file: /home/nicholai/Documents/dev/biohazard-vfx-website/src/middleware.ts + +
+
+ +--- + + +
Tool use: **read_file** • Read file: /home/nicholai/Documents/dev/biohazard-vfx-website/src/app/layout.tsx + +
+
+ +--- + + +
+ Tool use: **list_directory** • Listed directory undefined • **0** results + +No results found +
+
+ +--- + +Now I'll create the `.cursor/rules` directory and generate comprehensive Cursor rules based on the project structure and AGENTS.md guide: + + +--- + + +
Tool use: **command** • Run command: mkdir -p /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules + +```bash +mkdir -p /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules +``` +
+
+ +--- + +Now I'll create comprehensive Cursor rules based on the project structure and requirements: + + +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/project-structure.mdc + +**Chunk 1** +Lines added: 60, lines removed: 1 + +```diff +@@ -1,1 +1,60 @@ +- ++ --- ++ alwaysApply: true ++ --- ++ ++ # Biohazard VFX Website - Project Structure ++ ++ This is a Next.js 15.5.4 VFX studio website built with React 19, TypeScript, Tailwind CSS 4, and shadcn/ui components. ++ ++ ## Core Architecture ++ ++ - **Framework**: Next.js 15.5.4 with App Router ++ - **Styling**: Tailwind CSS 4 + shadcn/ui components ++ - **Animation**: Framer Motion for subtle transitions ++ - **Forms**: react-hook-form + Zod validation ++ - **Deployment**: Cloudflare Workers via OpenNext ++ - **Package Manager**: npm ++ ++ ## Project Layout ++ ++ ``` ++ src/ ++ ├─ app/ # App Router pages and layouts ++ │ ├─ (marketing)/ # Route groups ++ │ ├─ api/ # Route handlers ++ │ └─ layout.tsx # Root layout with global providers ++ ├─ components/ # Reusable UI components ++ │ └─ ui/ # shadcn/ui primitives ++ ├─ data/ # JSON/TS data objects ++ ├─ lib/ # Utilities, hooks, server actions ++ ├─ styles/ # globals.css, Tailwind utilities ++ └─ types/ # Shared TypeScript types ++ ``` ++ ++ ## Key Files ++ ++ - [AGENTS.md](mdc:AGENTS.md) - Single source of truth for development guidelines ++ - [package.json](mdc:package.json) - Dependencies and scripts ++ - [next.config.ts](mdc:next.config.ts) - Next.js configuration ++ - [tsconfig.json](mdc:tsconfig.json) - TypeScript configuration with @ alias ++ - [src/middleware.ts](mdc:src/middleware.ts) - Route protection and redirects ++ - [src/app/layout.tsx](mdc:src/app/layout.tsx) - Root layout with fonts and metadata ++ ++ ## Import Aliases ++ ++ Always use absolute imports with `@` mapped to `src/`: ++ ```typescript ++ import { Component } from '@/components/Component' ++ import { data } from '@/data/projects' ++ ``` ++ ++ ## Development Commands ++ ++ ```bash ++ npm ci # Install dependencies ++ npm run dev # Development server with Turbopack ++ npm run type-check # TypeScript validation ++ npm run lint # ESLint validation ++ npm run build # Production build ++ npx open-next@latest build # OpenNext build for Cloudflare ++ ``` +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/ui-system.mdc + +**Chunk 1** +Lines added: 60, lines removed: 1 + +```diff +@@ -1,1 +1,60 @@ +- ++ --- ++ globs: *.tsx,*.ts,*.css ++ --- ++ ++ # UI System Guidelines ++ ++ ## Theme & Design System ++ ++ - **Default Theme**: Dark mode only - do not introduce light-first designs ++ - **Typography**: Use CSS variables for fonts (Geist, Geist Mono, Bebas Neue, Orbitron, etc.) ++ - **Components**: Use shadcn/ui primitives from [src/components/ui/](mdc:src/components/ui/) ++ - **Spacing**: Follow Tailwind 4 defaults, prefer utility classes over custom CSS ++ - **Animation**: Keep Framer Motion subtle and meaningful only ++ ++ ## Component Structure ++ ++ ```typescript ++ // Use shadcn/ui primitives as base ++ import { Button } from '@/components/ui/button' ++ import { Card } from '@/components/ui/card' ++ ++ // Extend with local wrappers when needed ++ export function CustomComponent() { ++ return ( ++ <Card className="bg-black border-gray-800"> ++ <Button variant="outline" className="text-white"> ++ Action ++ </Button> ++ </Card> ++ ) ++ } ++ ``` ++ ++ ## Styling Rules ++ ++ 1. **Dark Theme**: All components must work in dark mode ++ 2. **Tailwind First**: Use utility classes before custom CSS ++ 3. **Component Variants**: Use class-variance-authority for component variants ++ 4. **Responsive**: Mobile-first responsive design ++ 5. **Accessibility**: Include proper ARIA labels and semantic HTML ++ ++ ## Font Usage ++ ++ Available font variables from [src/app/layout.tsx](mdc:src/app/layout.tsx): ++ - `--font-geist-sans` (default) ++ - `--font-geist-mono` ++ - `--font-bebas` ++ - `--font-orbitron` ++ - `--font-inter` ++ - `--font-jetbrains-mono` ++ - `--font-space-mono` ++ - `--font-rajdhani` ++ - `--font-exo-2` ++ ++ ## Animation Guidelines ++ ++ - Use Framer Motion sparingly for meaningful transitions ++ - Prefer CSS transitions for simple hover effects ++ - Keep animations under 300ms for UI feedback ++ - Respect `prefers-reduced-motion` for accessibility +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/routing-layout.mdc + +**Chunk 1** +Lines added: 67, lines removed: 1 + +```diff +@@ -1,1 +1,67 @@ +- ++ --- ++ globs: src/app/**/*.tsx ++ --- ++ ++ # Routing and Layout Rules ++ ++ ## App Router Structure ++ ++ - **Pages**: Live in `src/app/` directory ++ - **Server Components**: Default to server components, promote to client only when needed ++ - **Layout Hierarchy**: Root layout owns global providers, navigation, and footer ++ - **Route Groups**: Use `(marketing)` for grouped routes without affecting URL structure ++ ++ ## Layout Rules ++ ++ ### Root Layout ([src/app/layout.tsx](mdc:src/app/layout.tsx)) ++ - Owns global providers and theme class ++ - Contains `<Navigation />` and `<Footer />` components ++ - Sets up font variables and metadata ++ - **DO NOT** duplicate these in child layouts ++ ++ ### Page Layouts ++ - Keep server components as default ++ - Add `"use client"` only when necessary for interactivity ++ - Define unique metadata for each route ++ ++ ## Metadata Requirements ++ ++ Every page must have: ++ ```typescript ++ export const metadata: Metadata = { ++ title: "Unique Page Title", ++ description: "Unique description for SEO", ++ // Include Open Graph and Twitter cards ++ } ++ ``` ++ ++ ## Route Protection ++ ++ The [src/middleware.ts](mdc:src/middleware.ts) currently redirects all routes to `/` except: ++ - Home page (`/`) ++ - Next.js internal routes (`/_next/*`) ++ - Favicon files ++ - Specific static assets (OLIVER.jpeg, OLIVER_depth.jpeg, etc.) ++ ++ ## Static Assets ++ ++ When adding new files to `public/`, update the middleware allowlist in [src/middleware.ts](mdc:src/middleware.ts) line 8 to prevent 307 redirects. ++ ++ ## Common Patterns ++ ++ ```typescript ++ // Page component ++ export default function PageName() { ++ return ( ++ <div className="container mx-auto px-4"> ++ {/* Page content */} ++ </div> ++ ) ++ } ++ ++ // With metadata ++ export const metadata: Metadata = { ++ title: "Page Title", ++ description: "Page description" ++ } ++ ``` +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/forms-validation.mdc + +**Chunk 1** +Lines added: 87, lines removed: 1 + +```diff +@@ -1,1 +1,87 @@ +- ++ --- ++ globs: **/*form*.tsx,**/*Form*.tsx ++ --- ++ ++ # Forms and Validation ++ ++ ## Form Library Stack ++ ++ - **Forms**: react-hook-form for form state management ++ - **Validation**: Zod schemas for type-safe validation ++ - **Resolvers**: @hookform/resolvers for integration ++ ++ ## Form Structure ++ ++ ```typescript ++ import { useForm } from 'react-hook-form' ++ import { zodResolver } from '@hookform/resolvers/zod' ++ import { z } from 'zod' ++ ++ // Define schema ++ const formSchema = z.object({ ++ name: z.string().min(1, 'Name is required'), ++ email: z.string().email('Invalid email address'), ++ message: z.string().min(10, 'Message must be at least 10 characters') ++ }) ++ ++ type FormData = z.infer<typeof formSchema> ++ ++ export function ContactForm() { ++ const form = useForm<FormData>({ ++ resolver: zodResolver(formSchema), ++ defaultValues: { ++ name: '', ++ email: '', ++ message: '' ++ } ++ }) ++ ++ const onSubmit = (data: FormData) => { ++ // Handle form submission ++ } ++ ++ return ( ++ <form onSubmit={form.handleSubmit(onSubmit)}> ++ {/* Form fields with error handling */} ++ </form> ++ ) ++ } ++ ``` ++ ++ ## Error Handling Rules ++ ++ 1. **Field-level errors**: Show validation errors under each field ++ 2. **Generic submit error**: Display general submission errors ++ 3. **Never swallow errors**: Always surface validation and submission errors ++ 4. **Loading states**: Show loading indicators during submission ++ ++ ## Form Components ++ ++ Use shadcn/ui form components from [src/components/ui/form.tsx](mdc:src/components/ui/form.tsx): ++ ++ ```typescript ++ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form' ++ import { Input } from '@/components/ui/input' ++ import { Button } from '@/components/ui/button' ++ ++ <FormField ++ control={form.control} ++ name="email" ++ render={({ field }) => ( ++ <FormItem> ++ <FormLabel>Email</FormLabel> ++ <FormControl> ++ <Input placeholder="your@email.com" {...field} /> ++ </FormControl> ++ <FormMessage /> ++ </FormItem> ++ )} ++ /> ++ ``` ++ ++ ## Validation Patterns ++ ++ - Use Zod for all form validation ++ - Provide clear, user-friendly error messages ++ - Validate on both client and server side ++ - Handle async validation (e.g., email uniqueness) +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/images-assets.mdc + +**Chunk 1** +Lines added: 75, lines removed: 1 + +```diff +@@ -1,1 +1,75 @@ +- ++ --- ++ globs: **/*.tsx,**/*.ts ++ --- ++ ++ # Images and Assets ++ ++ ## Image Handling ++ ++ ### Next.js Image Component ++ Always use Next.js Image component for remote images: ++ ++ ```typescript ++ import Image from 'next/image' ++ ++ <Image ++ src="https://images.unsplash.com/photo-123" ++ alt="Descriptive alt text" ++ width={800} ++ height={600} ++ className="rounded-lg" ++ /> ++ ``` ++ ++ ### Remote Image Domains ++ Current allowed domains in [next.config.ts](mdc:next.config.ts): ++ - `images.unsplash.com` ++ ++ **When adding new external domains:** ++ 1. Add to `remotePatterns` in [next.config.ts](mdc:next.config.ts) ++ 2. Document the change in [AGENTS.md](mdc:AGENTS.md) ++ ++ ## Static Assets ++ ++ ### Public Directory ++ - Keep `public/` for truly static assets only ++ - Current assets: favicon files, images (OLIVER.jpeg, etc.), GIFs ++ ++ ### Middleware Whitelist ++ **CRITICAL**: When adding new static assets to `public/`, update the middleware allowlist in [src/middleware.ts](mdc:src/middleware.ts) line 8: ++ ++ ```typescript ++ // Add new asset paths here ++ if (pathname === '/' || ++ pathname.startsWith('/_next') || ++ pathname.startsWith('/favicon.') || ++ pathname === '/OLIVER.jpeg' || ++ pathname === '/new-asset.jpg' || // Add new assets here ++ pathname === '/reel.mp4') { ++ return NextResponse.next(); ++ } ++ ``` ++ ++ **Common symptom**: If assets return "text/html" Content-Type error, the path isn't whitelisted. ++ ++ ## Asset Optimization ++ ++ - Use appropriate image formats (WebP when possible) ++ - Provide proper alt text for accessibility ++ - Use responsive images with `sizes` prop ++ - Optimize file sizes for web delivery ++ ++ ## Video Files ++ ++ Custom headers are configured in [next.config.ts](mdc:next.config.ts) for `.mp4` files: ++ ```typescript ++ { ++ source: "/:path*.mp4", ++ headers: [ ++ { ++ key: "Content-Type", ++ value: "video/mp4", ++ }, ++ ], ++ } ++ ``` +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/deployment.mdc + +**Chunk 1** +Lines added: 73, lines removed: 1 + +```diff +@@ -1,1 +1,73 @@ +- ++ --- ++ alwaysApply: true ++ --- ++ ++ # Deployment and Build Process ++ ++ ## Cloudflare Workers with OpenNext ++ ++ This project deploys to Cloudflare Workers using OpenNext for Next.js compatibility. ++ ++ ### Build Process ++ ++ 1. **Quality Gates** (run before build): ++ ```bash ++ npm run type-check # TypeScript validation ++ npm run lint # ESLint validation ++ ``` ++ ++ 2. **Production Build**: ++ ```bash ++ npm run build # Next.js build ++ ``` ++ ++ 3. **OpenNext Build**: ++ ```bash ++ npx open-next@latest build # Generate Cloudflare-compatible build ++ ``` ++ ++ 4. **Deploy**: ++ ```bash ++ npx wrangler deploy .open-next/worker ++ ``` ++ ++ ### Configuration Files ++ ++ - [wrangler.toml](mdc:wrangler.toml) - Cloudflare Workers configuration ++ - [open-next.config.ts](mdc:open-next.config.ts) - OpenNext build configuration ++ - [next.config.ts](mdc:next.config.ts) - Next.js configuration ++ ++ ### Required wrangler.toml Settings ++ ++ ```toml ++ name = "site-worker" ++ main = ".open-next/worker/index.mjs" ++ compatibility_date = "2024-09-23" ++ compatibility_flags = ["nodejs_compat"] ++ assets = { directory = ".open-next/assets" } ++ ``` ++ ++ ## Environment Variables ++ ++ Create `.env.sample` and keep it synchronized. Typical keys: ++ ``` ++ NEXT_PUBLIC_SITE_URL= ++ RESEND_API_KEY= ++ CF_PAGES_URL= ++ ``` ++ ++ **Security**: Never commit real secrets. Use `.env` locally and environment variables in production. ++ ++ ## Build Configuration ++ ++ - ESLint and TypeScript errors are ignored during build for deployment speed ++ - CI still gates on `lint` and `type-check` before merge ++ - Always fix errors instead of bypassing checks ++ ++ ## Deployment Checklist ++ ++ - [ ] Run `npm run type-check` and `npm run lint` ++ - [ ] Ensure `assets.directory` matches OpenNext output ++ - [ ] Keep compatibility date at or after 2024-09-23 ++ - [ ] Test build locally with `npm run build` ++ - [ ] Verify OpenNext build artifacts in `.open-next/` +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/development-workflow.mdc + +**Chunk 1** +Lines added: 74, lines removed: 1 + +```diff +@@ -1,1 +1,74 @@ +- ++ --- ++ alwaysApply: true ++ --- ++ ++ # Development Workflow ++ ++ ## Git Workflow ++ ++ - **Default Branch**: `main` is protected ++ - **Workflow**: feature branches → PR → required checks → squash merge ++ - **Commit Format**: Conventional Commits ++ - `feat: add contact form schema` ++ - `fix: correct Image remote pattern` ++ - `chore: bump dependencies` ++ ++ ## Required Checks ++ ++ Before any merge: ++ - `npm run lint` - ESLint validation ++ - `npm run type-check` - TypeScript validation ++ - `npm run build` - Production build (optional locally, required in CI) ++ ++ ## Development Commands ++ ++ ```bash ++ # Setup ++ npm ci # Install dependencies ++ ++ # Development ++ npm run dev # Dev server with Turbopack ++ npm run type-check # TypeScript validation ++ npm run lint # ESLint validation ++ ++ # Build & Deploy ++ npm run build # Production build ++ npm run start # Preview production build ++ npx open-next@latest build # OpenNext build ++ npx wrangler deploy .open-next/worker # Deploy to Cloudflare ++ ``` ++ ++ ## Code Quality ++ ++ ### TypeScript ++ - Use strict mode (enabled in [tsconfig.json](mdc:tsconfig.json)) ++ - Prefer type inference over explicit types ++ - Use absolute imports with `@` alias ++ ++ ### ESLint ++ - Follow Next.js ESLint config ++ - Fix all linting errors before committing ++ - Don't bypass checks in production builds ++ ++ ## Testing Strategy ++ ++ - **Unit Tests**: Place close to sources, name with `.test.ts` or `.test.tsx` ++ - **E2E Tests**: Optional Playwright setup ++ - **Manual Testing**: Test all user flows before deployment ++ ++ ## Pull Request Guidelines ++ ++ - Keep PRs small and reviewable ++ - Include screenshots for UI changes ++ - Update [AGENTS.md](mdc:AGENTS.md) if conventions change ++ - Justify new dependencies in PR description ++ - Never commit secrets or sensitive data ++ ++ ## Common Pitfalls to Avoid ++ ++ 1. Adding remote image domains without updating [next.config.ts](mdc:next.config.ts) ++ 2. Introducing client components unnecessarily ++ 3. Duplicating navigation in nested layouts ++ 4. Bypassing Tailwind utilities for custom CSS ++ 5. Forgetting to update middleware whitelist for new static assets ++ 6. Committing secrets instead of using environment variables +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/data-content.mdc + +**Chunk 1** +Lines added: 97, lines removed: 1 + +```diff +@@ -1,1 +1,97 @@ +- ++ --- ++ globs: src/data/**/*.ts,src/data/**/*.json ++ --- ++ ++ # Data and Content Management ++ ++ ## Data Structure ++ ++ Non-secret content belongs in `src/data/` as TypeScript modules or JSON files. Keep data presentation-agnostic. ++ ++ ## Current Data Files ++ ++ - [src/data/projects.ts](mdc:src/data/projects.ts) - Project portfolio data ++ - [src/data/services.ts](mdc:src/data/services.ts) - Service offerings data ++ ++ ## Data Patterns ++ ++ ### TypeScript Data Modules ++ ```typescript ++ // src/data/projects.ts ++ export interface Project { ++ id: string ++ title: string ++ description: string ++ category: string ++ images: string[] ++ videoUrl?: string ++ } ++ ++ export const projects: Project[] = [ ++ { ++ id: 'project-1', ++ title: 'Project Title', ++ description: 'Project description', ++ category: 'commercial', ++ images: ['/image1.jpg', '/image2.jpg'] ++ } ++ ] ++ ``` ++ ++ ### JSON Data Files ++ ```json ++ { ++ "services": [ ++ { ++ "id": "vfx", ++ "name": "Visual Effects", ++ "description": "High-end VFX services" ++ } ++ ] ++ } ++ ``` ++ ++ ## Data Usage Rules ++ ++ 1. **Server Components**: Prefer server components for data fetching ++ 2. **File Imports**: Use direct imports instead of client-side fetching for static data ++ 3. **Type Safety**: Define TypeScript interfaces for all data structures ++ 4. **Separation**: Keep data separate from presentation logic ++ ++ ## Content Guidelines ++ ++ - Use descriptive, SEO-friendly content ++ - Include proper alt text for images ++ - Maintain consistent naming conventions ++ - Keep content up-to-date and accurate ++ ++ ## Data Fetching Patterns ++ ++ ```typescript ++ // ✅ Good: Server component with direct import ++ import { projects } from '@/data/projects' ++ ++ export default function PortfolioPage() { ++ return ( ++ <div> ++ {projects.map(project => ( ++ <ProjectCard key={project.id} project={project} /> ++ ))} ++ </div> ++ ) ++ } ++ ++ // ❌ Avoid: Client-side fetching of static data ++ 'use client' ++ import { useEffect, useState } from 'react' ++ ++ export function PortfolioPage() { ++ const [projects, setProjects] = useState([]) ++ ++ useEffect(() => { ++ fetch('/api/projects').then(res => res.json()) ++ }, []) ++ ++ // ... ++ } ++ ``` +``` + +
+ +
+ +--- + +Now let me create a few more specialized rules for specific aspects of the project: + + +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/seo-metadata.mdc + +**Chunk 1** +Lines added: 78, lines removed: 1 + +```diff +@@ -1,1 +1,78 @@ +- ++ --- ++ globs: src/app/**/page.tsx,src/app/**/layout.tsx ++ --- ++ ++ # SEO and Metadata ++ ++ ## Metadata API Requirements ++ ++ Every page must define unique metadata using the Next.js Metadata API: ++ ++ ```typescript ++ import type { Metadata } from "next" ++ ++ export const metadata: Metadata = { ++ title: "Unique Page Title | Biohazard VFX", ++ description: "Unique, descriptive page description for SEO", ++ metadataBase: new URL("https://biohazardvfx.com"), ++ openGraph: { ++ title: "Page Title", ++ description: "Page description", ++ type: "website", ++ locale: "en_US", ++ siteName: "Biohazard VFX", ++ }, ++ twitter: { ++ card: "summary_large_image", ++ title: "Page Title", ++ description: "Page description", ++ }, ++ } ++ ``` ++ ++ ## Root Layout Metadata ++ ++ The root layout in [src/app/layout.tsx](mdc:src/app/layout.tsx) includes: ++ - Global site metadata ++ - Open Graph configuration ++ - Twitter card setup ++ - JSON-LD structured data ++ - Canonical URLs ++ ++ ## SEO Best Practices ++ ++ 1. **Unique Titles**: Each page must have a unique, descriptive title ++ 2. **Descriptions**: Write compelling meta descriptions (150-160 characters) ++ 3. **Structured Data**: Use JSON-LD for rich snippets ++ 4. **Canonical URLs**: Set canonical URLs to prevent duplicate content ++ 5. **Open Graph**: Include OG tags for social media sharing ++ 6. **Twitter Cards**: Configure Twitter card metadata ++ ++ ## Structured Data Example ++ ++ ```typescript ++ const jsonLd = { ++ "@context": "https://schema.org", ++ "@type": "Organization", ++ name: "Biohazard VFX", ++ description: "Visual effects studio", ++ url: "https://biohazardvfx.com", ++ logo: "https://biohazardvfx.com/logo.png", ++ sameAs: ["https://instagram.com/biohazardvfx"], ++ } ++ ``` ++ ++ ## Page-Specific Metadata ++ ++ - **Home**: Focus on main services and value proposition ++ - **Portfolio**: Highlight featured projects and capabilities ++ - **Services**: Target specific service keywords ++ - **About**: Include company information and team details ++ - **Contact**: Include location and contact information ++ ++ ## Image SEO ++ ++ - Use descriptive alt text for all images ++ - Optimize image file names ++ - Include image dimensions ++ - Use appropriate image formats (WebP when possible) +``` + +
+ +
+ +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/component-patterns.mdc + +**Chunk 1** +Lines added: 159, lines removed: 1 + +```diff +@@ -1,1 +1,159 @@ +- ++ --- ++ globs: src/components/**/*.tsx ++ --- ++ ++ # Component Patterns ++ ++ ## Component Structure ++ ++ ### File Organization ++ - Place reusable components in `src/components/` ++ - Use PascalCase for component files: `ComponentName.tsx` ++ - Group related components in subdirectories when needed ++ ++ ### Component Template ++ ```typescript ++ import { cn } from '@/lib/utils' ++ ++ interface ComponentProps { ++ className?: string ++ children?: React.ReactNode ++ // Other props ++ } ++ ++ export function ComponentName({ ++ className, ++ children, ++ ...props ++ }: ComponentProps) { ++ return ( ++ <div className={cn("base-styles", className)} {...props}> ++ {children} ++ </div> ++ ) ++ } ++ ``` ++ ++ ## shadcn/ui Integration ++ ++ ### Using shadcn/ui Components ++ ```typescript ++ import { Button } from '@/components/ui/button' ++ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' ++ import { Badge } from '@/components/ui/badge' ++ ++ export function ProjectCard({ project }: { project: Project }) { ++ return ( ++ <Card className="bg-black border-gray-800"> ++ <CardHeader> ++ <CardTitle className="text-white">{project.title}</CardTitle> ++ </CardHeader> ++ <CardContent> ++ <Badge variant="outline">{project.category}</Badge> ++ </CardContent> ++ </Card> ++ ) ++ } ++ ``` ++ ++ ### Extending shadcn/ui Components ++ ```typescript ++ // Create wrapper components for common patterns ++ import { Button } from '@/components/ui/button' ++ import { cn } from '@/lib/utils' ++ ++ interface PrimaryButtonProps extends React.ComponentProps<typeof Button> { ++ loading?: boolean ++ } ++ ++ export function PrimaryButton({ ++ loading, ++ className, ++ children, ++ ...props ++ }: PrimaryButtonProps) { ++ return ( ++ <Button ++ className={cn("bg-primary hover:bg-primary/90", className)} ++ disabled={loading} ++ {...props} ++ > ++ {loading ? "Loading..." : children} ++ </Button> ++ ) ++ } ++ ``` ++ ++ ## Animation Patterns ++ ++ ### Framer Motion Usage ++ ```typescript ++ import { motion } from 'framer-motion' ++ ++ export function AnimatedCard({ children }: { children: React.ReactNode }) { ++ return ( ++ <motion.div ++ initial={{ opacity: 0, y: 20 }} ++ animate={{ opacity: 1, y: 0 }} ++ transition={{ duration: 0.3 }} ++ className="card-styles" ++ > ++ {children} ++ </motion.div> ++ ) ++ } ++ ``` ++ ++ ## Component Composition ++ ++ ### Compound Components ++ ```typescript ++ // Parent component ++ export function Accordion({ children }: { children: React.ReactNode }) { ++ return <div className="accordion-container">{children}</div> ++ } ++ ++ // Child components ++ export function AccordionItem({ children }: { children: React.ReactNode }) { ++ return <div className="accordion-item">{children}</div> ++ } ++ ++ export function AccordionTrigger({ children }: { children: React.ReactNode }) { ++ return <button className="accordion-trigger">{children}</button> ++ } ++ ++ // Usage ++ <Accordion> ++ <AccordionItem> ++ <AccordionTrigger>Title</AccordionTrigger> ++ <AccordionContent>Content</AccordionContent> ++ </AccordionItem> ++ </Accordion> ++ ``` ++ ++ ## Props and TypeScript ++ ++ ### Interface Patterns ++ ```typescript ++ // Use descriptive interface names ++ interface ProjectCardProps { ++ project: Project ++ variant?: 'default' | 'featured' ++ showDescription?: boolean ++ onSelect?: (project: Project) => void ++ } ++ ++ // Use React.ComponentProps for extending HTML elements ++ interface CustomButtonProps extends React.ComponentProps<'button'> { ++ variant?: 'primary' | 'secondary' ++ size?: 'sm' | 'md' | 'lg' ++ } ++ ``` ++ ++ ## Styling Guidelines ++ ++ 1. **Tailwind First**: Use utility classes before custom CSS ++ 2. **Conditional Classes**: Use `cn()` utility for conditional styling ++ 3. **Responsive Design**: Mobile-first approach with responsive utilities ++ 4. **Dark Theme**: Ensure all components work in dark mode ++ 5. **Accessibility**: Include proper ARIA labels and semantic HTML +``` + +
+ +
+ +--- + +Error calling tool 'write'. + +--- + +Let me create the final rule file: + + +--- + + + +
Tool use: **code_edit** • Edit file: /home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/vfx-specific.mdc + +**Chunk 1** +Lines added: 99, lines removed: 1 + +```diff +@@ -1,1 +1,99 @@ +- ++ --- ++ description: VFX studio specific patterns and requirements ++ --- ++ ++ # VFX Studio Specific Guidelines ++ ++ ## Media Handling ++ ++ ### Video Components ++ - Use [src/components/VideoPlayer.tsx](mdc:src/components/VideoPlayer.tsx) for video playback ++ - Use [src/components/ReelPlayer.tsx](mdc:src/components/ReelPlayer.tsx) for demo reels ++ - Support multiple video formats (MP4, WebM) ++ - Include proper video metadata and thumbnails ++ ++ ### Image Components ++ - Use [src/components/DepthMap.tsx](mdc:src/components/DepthMap.tsx) for depth map visualizations ++ - Implement lazy loading for portfolio images ++ - Use Next.js Image optimization for all media ++ ++ ## Portfolio Patterns ++ ++ ### Project Showcase ++ ```typescript ++ // Use ProjectCard component for consistent project display ++ import { ProjectCard } from '@/components/ProjectCard' ++ import { ProjectShowcase } from '@/components/ProjectShowcase' ++ ++ // Project data structure from src/data/projects.ts ++ interface Project { ++ id: string ++ title: string ++ description: string ++ category: 'commercial' | 'music-video' | 'film' | 'animation' ++ client?: string ++ year: number ++ images: string[] ++ videoUrl?: string ++ tags: string[] ++ } ++ ``` ++ ++ ### Service Categories ++ - Visual Effects (VFX) ++ - Motion Graphics ++ - 3D Animation ++ - Compositing ++ - Color Grading ++ - Post-Production ++ ++ ## Client Work Patterns ++ ++ ### Client Logo Grid ++ - Use [src/components/ClientLogoGrid.tsx](mdc:src/components/ClientLogoGrid.tsx) ++ - Display client logos with proper attribution ++ - Ensure logos are high-quality and properly sized ++ ++ ### Project Filtering ++ - Implement category-based filtering ++ - Support tag-based search ++ - Include year-based sorting ++ ++ ## Performance Considerations ++ ++ ### Media Optimization ++ - Compress images and videos for web delivery ++ - Use appropriate formats (WebP for images, MP4 for videos) ++ - Implement progressive loading for large media files ++ - Use CDN for media delivery ++ ++ ### Loading States ++ - Show skeleton loaders for media content ++ - Implement progressive image loading ++ - Use intersection observer for lazy loading ++ ++ ## VFX-Specific UI Elements ++ ++ ### Before/After Comparisons ++ - Implement split-screen comparisons ++ - Use slider controls for reveal effects ++ - Include toggle for before/after views ++ ++ ### Process Showcases ++ - Show breakdowns of VFX work ++ - Include wireframe and final render comparisons ++ - Display technical specifications ++ ++ ## Content Guidelines ++ ++ ### Project Descriptions ++ - Include technical details (software used, techniques) ++ - Mention client and project scope ++ - Highlight challenges and solutions ++ - Use industry-standard terminology ++ ++ ### Service Descriptions ++ - Be specific about capabilities ++ - Include typical project timelines ++ - Mention software and hardware capabilities ++ - Provide clear pricing structure (if applicable) +``` + +
+ +
+ +--- + +Now let me verify the created rules and check for any linting issues: + + +--- + + +
+ Tool use: **list_directory** • Listed directory undefined • **0** results + +No results found +
+
+ +--- + + +
+ Tool use: **read_lints** • Read lints for 1 file + +Lint paths: + +- `/home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules` + +Lint results + +Lint errors for `.cursor/rules/project-structure.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD040/fenced-code-language: Fenced code blocks should have a language specified | 20:1-20:4 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 46:1-46:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 60:3-60:4 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/ui-system.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 45:1-45:32 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 60:52-60:53 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/routing-layout.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 16:1-16:63 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 17:1-17:40 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 22:1-22:17 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 23:1-23:36 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 30:1-30:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 41:1-41:18 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 67:3-67:4 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/forms-validation.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD047/single-trailing-newline: Files should end with a single newline character | 87:50-87:51 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/images-assets.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 9:1-9:28 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 24:1-24:25 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 26:1-26:24 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 29:1-29:67 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 34:1-34:21 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 35:1-35:46 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 38:1-38:25 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 65:1-65:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 75:3-75:4 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/deployment.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 14:1-14:11 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 20:1-20:11 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 25:1-25:11 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 30:1-30:11 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 53:1-53:4 | DIAGNOSTIC_SEVERITY_WARNING | +| MD040/fenced-code-language: Fenced code blocks should have a language specified | 53:1-53:4 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 73:54-73:55 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/development-workflow.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 19:1-19:37 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 43:1-43:15 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 44:1-44:66 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 48:1-48:11 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 49:1-49:31 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 74:60-74:61 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/data-content.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 18:1-18:28 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 19:1-19:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 41:1-41:20 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 42:1-42:8 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 97:3-97:4 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/seo-metadata.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 36:1-36:23 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 78:52-78:53 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/component-patterns.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 9:1-9:22 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 10:1-10:49 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 14:1-14:23 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 15:1-15:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 39:1-39:31 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 40:1-40:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 59:1-59:35 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 60:1-60:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 89:1-89:24 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 90:1-90:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 109:1-109:24 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 110:1-110:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 136:1-136:23 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 137:1-137:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 159:66-159:67 | DIAGNOSTIC_SEVERITY_WARNING | + + +Lint errors for `.cursor/rules/vfx-specific.mdc` + + +| Message | Range | Severity | +|-------|-------|----------| +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 9:1-9:21 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 10:1-10:94 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 15:1-15:21 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 16:1-16:98 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 22:1-22:21 | DIAGNOSTIC_SEVERITY_WARNING | +| MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines | 23:1-23:14 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 42:1-42:23 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 43:1-43:23 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 52:1-52:21 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 53:1-53:81 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 57:1-57:22 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 58:1-58:37 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 64:1-64:23 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 65:1-65:46 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 70:1-70:19 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 71:1-71:42 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 77:1-77:29 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 78:1-78:37 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 82:1-82:22 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 83:1-83:30 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 89:1-89:25 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 90:1-90:56 | DIAGNOSTIC_SEVERITY_WARNING | +| MD022/blanks-around-headings: Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] | 95:1-95:25 | DIAGNOSTIC_SEVERITY_WARNING | +| MD032/blanks-around-lists: Lists should be surrounded by blank lines | 96:1-96:33 | DIAGNOSTIC_SEVERITY_WARNING | +| MD047/single-trailing-newline: Files should end with a single newline character | 99:49-99:50 | DIAGNOSTIC_SEVERITY_WARNING | + + +
+
+ +--- + +The linting errors are mostly markdown formatting issues (missing blank lines, trailing newlines, etc.). These are minor formatting issues that don't affect functionality. Let me create a summary of what I've accomplished: + +## ✅ Cursor Rules Generated Successfully + +I've created a comprehensive set of Cursor rules for the Biohazard VFX website project. Here's what was generated: + +### 📁 Created Rules Directory +- `/home/nicholai/Documents/dev/biohazard-vfx-website/.cursor/rules/` + +### 📋 Generated Rule Files (11 total) + +1. **`project-structure.mdc`** (Always Applied) + - Core architecture overview + - Tech stack details + - Project layout and key files + - Import aliases and development commands + +2. **`ui-system.mdc`** (Applied to TSX/TS/CSS files) + - Dark theme guidelines + - shadcn/ui component usage + - Typography and animation rules + - Styling best practices + +3. **`routing-layout.mdc`** (Applied to App Router files) + - App Router structure + - Layout hierarchy rules + - Metadata requirements + - Route protection patterns + +4. **`forms-validation.mdc`** (Applied to form components) + - react-hook-form + Zod patterns + - Error handling guidelines + - Form component structure + - Validation best practices + +5. **`images-assets.mdc`** (Applied to TSX/TS files) + - Next.js Image component usage + - Remote image domain management + - Static asset handling + - Middleware whitelist requirements + +6. **`deployment.mdc`** (Always Applied) + - Cloudflare Workers deployment + - OpenNext build process + - Environment variables + - Build configuration + +7. **`development-workflow.mdc`** (Always Applied) + - Git workflow and commit conventions + - Required checks and quality gates + - Development commands + - PR guidelines + +8. **`data-content.mdc`** (Applied to data files) + - Data structure patterns + - TypeScript interfaces + - Content management guidelines + - Data fetching patterns + +9. **`seo-metadata.mdc`** (Applied to page/layout files) + - Metadata API requirements + - SEO best practices + - Structured data patterns + - Page-specific metadata + +10. **`component-patterns.mdc`** (Applied to component files) + - Component structure templates + - shadcn/ui integration + - Animation patterns + - TypeScript interface patterns + +11. **`vfx-specific.mdc`** (Manual application) + - VFX studio specific patterns + - Media handling guidelines + - Portfolio and service patterns + - Performance considerations + +### 🎯 Key Features + +- **Always Applied Rules**: Core project structure, deployment, and workflow rules +- **File-Specific Rules**: Targeted rules for different file types and components +- **Manual Rules**: VFX-specific patterns that can be referenced when needed +- **Cross-References**: Rules reference actual project files using `mdc:` syntax +- **Comprehensive Coverage**: From development workflow to deployment and VFX-specific patterns + +### 📝 Note on Linting + +The markdown linting warnings are minor formatting issues (missing blank lines, trailing newlines) that don't affect the functionality of the rules. The rules are fully functional and will help guide development according to the project's established patterns and the AGENTS.md guidelines. + +These rules will now help Cursor understand the project structure, coding patterns, and development workflow, making it much more effective at assisting with development tasks on this VFX studio website. + +--- + diff --git a/design.json b/design.json new file mode 100644 index 0000000..347d1b1 --- /dev/null +++ b/design.json @@ -0,0 +1,272 @@ +{ + "version": "1.0.0", + "name": "BIOHAZARD VFX Website Design System", + "description": "Design system for the BIOHAZARD VFX website based on Temp-Placeholder component", + "colorPalette": { + "background": { + "primary": "#000000", + "secondary": "#0a0a0a", + "description": "Primary black backgrounds with very dark secondary" + }, + "text": { + "primary": "#ffffff", + "secondary": "#e5e5e5", + "muted": "#a3a3a3", + "subtle": "#808080", + "verySubtle": "#606060", + "description": "White primary text with decreasing opacity gray variants" + }, + "accent": { + "primary": "#ff4d00", + "description": "Orange accent color used for interactive elements, links, and highlights" + }, + "borders": { + "subtle": "rgba(255, 255, 255, 0.05)", + "muted": "rgba(255, 255, 255, 0.1)", + "description": "Subtle white borders with low opacity for divisions" + }, + "overlay": { + "dark": "rgba(0, 0, 0, 0.8)", + "description": "Dark overlay for modals and overlays" + } + }, + "typography": { + "fontFamilies": { + "exo2": "font-exo-2", + "geist": "Geist, sans-serif", + "geistMono": "Geist Mono, monospace" + }, + "scales": { + "xl": { + "sizes": ["9xl", "8xl", "7xl", "6xl", "5xl"], + "description": "Extra large heading sizes for hero/main title (BIOHAZARD)" + }, + "lg": { + "sizes": ["4xl", "3xl"], + "description": "Large heading sizes for section titles" + }, + "base": { + "sizes": ["lg", "base", "sm"], + "description": "Base text sizes for body content" + }, + "xs": { + "sizes": ["xs"], + "description": "Extra small text for meta information" + } + }, + "weights": { + "normal": 400, + "bold": 700, + "black": 900, + "description": "Font weights used throughout the design" + }, + "lineHeight": { + "tight": "1.2", + "relaxed": "1.6", + "description": "Line heights for text readability" + } + }, + "spacing": { + "container": { + "maxWidth": "900px", + "padding": { + "mobile": "px-4", + "sm": "sm:px-6", + "lg": "lg:px-8" + }, + "description": "Main container width and responsive padding" + }, + "sections": { + "vertical": { + "small": "mb-8", + "medium": "md:mb-16", + "large": "md:mb-20", + "description": "Vertical spacing between major sections" + }, + "horizontal": { + "gap": "gap-6", + "description": "Horizontal gaps between elements" + } + }, + "card": { + "padding": { + "base": "p-6", + "sm": "sm:p-8", + "md": "md:p-12" + }, + "description": "Card container padding (main content area)" + }, + "elements": { + "small": "mb-4", + "medium": "mb-6", + "large": "mb-8", + "description": "Element spacing within sections" + } + }, + "breakpoints": { + "mobile": "< 640px", + "sm": "640px", + "md": "768px", + "lg": "1024px", + "xl": "1280px", + "description": "Tailwind CSS responsive breakpoints used" + }, + "components": { + "navigation": { + "description": "Top navigation bar", + "layout": "flex justify-between items-center", + "padding": "py-6", + "border": "border-b border-white/10", + "typography": "text-lg font-mono tracking-tight", + "interactive": { + "links": "hover:text-[#ff4d00] transition-colors", + "gap": "gap-6", + "fontSize": "text-sm" + } + }, + "card": { + "description": "Main content card container", + "background": "#0a0a0a", + "border": "border border-white/5", + "layout": "relative bg-[#0a0a0a] border border-white/5" + }, + "heading": { + "main": { + "description": "Large BIOHAZARD heading with text shadow effect", + "fontSize": ["text-3xl", "sm:text-4xl", "md:text-5xl"], + "fontFamily": "font-exo-2", + "fontWeight": "font-black", + "color": "#000000", + "textShadow": "2px 2px 0px #ff4d00, 4px 4px 0px #ff4d00", + "interactive": "hover:opacity-80 cursor-pointer transition-opacity" + }, + "heroTitle": { + "description": "Hero section title", + "fontSize": ["text-4xl", "sm:text-5xl", "md:text-7xl", "lg:text-8xl", "xl:text-9xl"], + "fontWeight": "font-black", + "fontFamily": "font-exo-2" + } + }, + "link": { + "description": "Interactive link styling", + "color": "#ff4d00", + "hover": "hover:opacity-80", + "underline": { + "description": "Animated underline on hover", + "height": "h-[1px]", + "animation": "scaleX animation on hover" + } + }, + "divider": { + "description": "Section divider component", + "type": "SectionDivider" + }, + "accordion": { + "description": "Horizontal expandable accordion", + "type": "HorizontalAccordion" + }, + "videoPlayer": { + "description": "Reel video player component", + "type": "ReelPlayer" + } + }, + "layout": { + "sections": [ + { + "name": "Navigation", + "id": "nav", + "content": "Brand name and navigation links" + }, + { + "name": "About", + "id": "about", + "content": "Hero message with accordion and main title" + }, + { + "name": "Work", + "id": "work", + "content": "Reel player and project list with video previews" + }, + { + "name": "Studio", + "id": "studio", + "content": "Instagram feed component" + }, + { + "name": "Contact", + "id": "contact", + "content": "Contact email and footer information" + } + ] + }, + "animations": { + "containerVariants": { + "hidden": "opacity: 0", + "visible": { + "opacity": 1, + "staggerChildren": 0.1, + "delayChildren": 0.1 + }, + "description": "Page load animation with stagger effect" + }, + "itemVariants": { + "hidden": "opacity: 0, y: 20", + "visible": "opacity: 1, y: 0", + "transition": "duration: 0.4, ease: easeOut", + "description": "Individual item fade-in and slide-up animation" + }, + "underlineAnimation": { + "initial": "scaleX: 0", + "hover": "scaleX: 1", + "transition": "duration: 0.3, ease: easeOut", + "description": "Animated underline on links" + }, + "easterEgg": { + "initial": "opacity: 0, scale: 0.7", + "animate": "opacity: 1, scale: 1", + "transition": "duration: 0.4, ease: [0.16, 1, 0.3, 1]", + "description": "Modal popup animation for easter eggs" + } + }, + "interactions": { + "links": { + "color": "#ff4d00", + "hoverEffect": "opacity 0.8, text-shadow glow", + "tapEffect": "scale 0.98", + "underlineAnimation": true, + "description": "Standard link interaction pattern" + }, + "easterEgg": { + "trigger": "Click on main BIOHAZARD heading or footer text", + "action": "Display modal with depth map or easter egg image", + "closeAction": "Click outside or mouse leave", + "description": "Hidden interactive elements" + }, + "hover": { + "cards": "opacity reduction on hover", + "text": "color change to accent color or text-shadow glow" + } + }, + "responsiveness": { + "strategy": "Mobile-first with progressive enhancement", + "mobileOptimizations": { + "fontSize": "Capped scaling to prevent cramped text", + "maxScale": 0.8, + "description": "Mobile (< 640px) uses conservative font scaling" + }, + "tabletOptimizations": { + "maxScale": 1.2, + "description": "Tablet (640-1024px) allows moderate scaling" + }, + "desktopOptimizations": { + "maxScale": 1.8, + "description": "Desktop (> 1024px) allows expansive scaling" + } + }, + "accessibility": { + "colorContrast": "High contrast white text on black backgrounds", + "interactiveElements": "Clear hover states and cursor pointers", + "semanticHTML": "Proper heading hierarchy and section landmarks", + "focus": "Default browser focus states on interactive elements" + } +} diff --git a/open-next.config.ts b/open-next.config.ts index 711bb23..7cd62c7 100644 --- a/open-next.config.ts +++ b/open-next.config.ts @@ -1,7 +1,7 @@ const config = { default: { override: { - wrapper: "cloudflare", + wrapper: "cloudflare-node", converter: "edge", proxyExternalRequest: "fetch", incrementalCache: "dummy", @@ -13,7 +13,7 @@ const config = { middleware: { external: true, override: { - wrapper: "cloudflare", + wrapper: "cloudflare-edge", converter: "edge", proxyExternalRequest: "fetch", incrementalCache: "dummy", diff --git a/src/app/api/media/[...path]/route.ts b/src/app/api/media/[...path]/route.ts index abb626b..d8cc50c 100644 --- a/src/app/api/media/[...path]/route.ts +++ b/src/app/api/media/[...path]/route.ts @@ -2,13 +2,14 @@ import { NextRequest, NextResponse } from 'next/server'; export async function GET( request: NextRequest, - { params }: { params: { path: string[] } } + { params }: { params: Promise<{ path: string[] }> } ) { try { - const path = params.path.join('/'); + const resolvedParams = await params; + const path = resolvedParams.path.join('/'); - // @ts-ignore - MEDIA is bound via wrangler.toml and available in the Cloudflare context - const cloudflareContext = (globalThis as any)[Symbol.for('__cloudflare-context__')]; + // @ts-expect-error - MEDIA is bound via wrangler.toml and available in the Cloudflare context + const cloudflareContext = (globalThis as Record)[Symbol.for('__cloudflare-context__')]; const MEDIA = cloudflareContext?.env?.MEDIA; if (!MEDIA) { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 109a82e..6b7e471 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono, Bebas_Neue, Orbitron, Inter, JetBrains_Mono, Space_Mono, Rajdhani, Exo_2 } from "next/font/google"; +import { Geist, Geist_Mono, Bebas_Neue, Orbitron, Inter, JetBrains_Mono, Space_Mono, Space_Grotesk, Rajdhani, Exo_2 } from "next/font/google"; import "./globals.css"; import { Navigation } from "@/components/Navigation"; import { Footer } from "@/components/Footer"; @@ -63,6 +63,14 @@ const rajdhani = Rajdhani({ preload: true, }); +const spaceGrotesk = Space_Grotesk({ + variable: "--font-space-grotesk", + subsets: ["latin"], + weight: ["400", "500", "600", "700"], + display: "swap", + preload: true, +}); + const exo2 = Exo_2({ variable: "--font-exo-2", subsets: ["latin"], @@ -124,7 +132,7 @@ export default function RootLayout({ />
{children} diff --git a/src/app/projects/speakers/page.tsx b/src/app/projects/speakers/page.tsx new file mode 100644 index 0000000..ce541f6 --- /dev/null +++ b/src/app/projects/speakers/page.tsx @@ -0,0 +1,19 @@ +import { Metadata } from 'next'; +import { SpeakersPageClient } from './speakers-client'; + +export const metadata: Metadata = { + title: 'SPEAKERS | Biohazard VFX', + description: '3D visualization gallery', + alternates: { + canonical: '/projects/speakers', + }, + openGraph: { + title: 'SPEAKERS | Biohazard VFX', + description: '3D visualization gallery.', + type: 'website', + }, +}; + +export default function SpeakersPage() { + return ; +} diff --git a/src/app/projects/speakers/speakers-client.tsx b/src/app/projects/speakers/speakers-client.tsx new file mode 100644 index 0000000..4c3e267 --- /dev/null +++ b/src/app/projects/speakers/speakers-client.tsx @@ -0,0 +1,164 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { PolycamEmbed } from '@/components/PolycamEmbed'; +import { speakers } from '@/data/speakers'; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.1, + }, + }, +}; + +const itemVariants = { + hidden: { + opacity: 0, + y: 20, + }, + visible: { + opacity: 1, + y: 0, + }, +}; + +export function SpeakersPageClient() { + return ( +
+ {/* Header Card */} +
+
+ +
+ {/* Title */} + + SPEAKERS + + + {/* Info Grid - Left Aligned */} +
+ +

Code

+

SPKR

+
+ + +

Client

+

Carly Gibert

+
+ + +

Studio

+

Biohazard VFX

+
+
+
+
+
+
+ + {/* Divider */} +
+
+
+
+
+ + {/* Embeds Section */} +
+
+ {/* Visually hidden heading for screen readers */} +

3D Scan Gallery

+ + + {speakers.map((scan, index) => ( + + ))} + +
+
+ + {/* Divider */} +
+
+
+
+
+ + {/* Footer */} +
+
+
+
+

Biohazard VFX

+

+ Artists and technical people specializing in VFX and 3D visualization. +

+
+ + contact@biohazardvfx.com + +
+
+
+
+ ); +} diff --git a/src/components/PolycamEmbed.tsx b/src/components/PolycamEmbed.tsx new file mode 100644 index 0000000..9c7106a --- /dev/null +++ b/src/components/PolycamEmbed.tsx @@ -0,0 +1,172 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; + +interface PolycamEmbedProps { + captureId: string; + title: string; + index?: number; +} + +export function PolycamEmbed({ captureId, title, index = 0 }: PolycamEmbedProps) { + const [isFullscreen, setIsFullscreen] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + const [isDesktop, setIsDesktop] = useState(true); + + useEffect(() => { + const checkDesktop = () => { + setIsDesktop(window.innerWidth >= 1024); + }; + + checkDesktop(); + window.addEventListener('resize', checkDesktop); + return () => window.removeEventListener('resize', checkDesktop); + }, []); + + const itemVariants = { + hidden: { + opacity: 0, + y: 20, + }, + visible: { + opacity: 1, + y: 0, + }, + }; + + return ( + <> + + {/* Regular Embed */} +
+ {/* Loading Skeleton */} + {isLoading && !hasError && ( +
+ )} + + {/* Error State */} + {hasError && ( +
+
+

Failed to load 3D scan

+

Please try again later

+
+
+ )} + +