From 21b80c46c582c5b1d7e2a3f71082e0e0e480cddb Mon Sep 17 00:00:00 2001 From: benbaessler Date: Wed, 3 Sep 2025 02:53:29 +0200 Subject: [PATCH 01/30] feat: claude.md file --- CLAUDE.md | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1d6ca53 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,159 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Product Overview + +**Mirror Studio** is an AI-powered virtual try-on application that solves the online shopping experience problem for fashion e-commerce. The application allows users to visualize how clothing items would look on diverse AI-generated models before making purchase decisions. + +### Problem It Solves +- **For Shoppers**: Reduces uncertainty when buying clothes online by showing realistic visualizations of garments on models with customizable body types, ethnicities, and ages +- **For E-commerce Businesses**: Decreases return rates and increases conversion by providing interactive product visualization +- **For Fashion Brands**: Eliminates the cost and time of traditional photoshoots while showcasing products on diverse models + +### Key Features +- Upload multiple clothing items to create complete outfits +- Customize model appearance (gender, ethnicity, age, pose) +- Generate photorealistic virtual try-on images using Google's Gemini AI +- Secure image downloads with authentication +- Professional studio-quality backgrounds + +## Development Commands + +```bash +# Development server (with Turbopack) +bun dev +# or +npm run dev + +# Production build +bun run build +# or +npm run build + +# Start production server +bun start +# or +npm start + +# Type checking (no built-in script, add if needed) +bunx tsc --noEmit +# or +npx tsc --noEmit +``` + +## Architecture Overview + +### Tech Stack +- **Framework**: Next.js 15 with App Router and Turbopack +- **Authentication**: Clerk (managed auth with modal-based UI) +- **AI/ML**: Google Gemini API (gemini-2.5-flash-image-preview model) +- **Styling**: Tailwind CSS v4 +- **Type Safety**: TypeScript with strict mode +- **State Management**: useReducer pattern with typed actions + +### Directory Structure +``` +app/ +├── api/generate/route.ts # Protected API endpoint for Gemini +├── MirrorStudioApp.tsx # Main app component (client-side) +├── page.tsx # Dynamic import wrapper (SSR bypass) +└── layout.tsx # Root layout with Clerk provider + +components/ # Reusable UI components +hooks/ # Custom React hooks (useVirtualTryOn) +lib/ # Server utilities (Gemini integration) +services/ # Client-side services +types.ts # Centralized TypeScript definitions +``` + +### Key Architectural Patterns + +#### 1. Server-Side API Protection +The Gemini API key is NEVER exposed to the client. All AI operations go through `/api/generate/route.ts`: +```typescript +// Client makes request with base64 images +POST /api/generate → Server validates → Server calls Gemini → Returns generated image +``` + +#### 2. Authentication Flow +- Clerk handles all auth via modal dialogs (no dedicated auth pages) +- Download feature requires authentication (triggers sign-in modal if not authenticated) +- User authentication state checked via `useAuth()` and `useClerk()` hooks + +#### 3. State Management Pattern +Single reducer manages entire app state with typed actions: +- `AppState`: Complete application state +- `AppAction`: Union type of all possible actions +- State transitions are predictable and type-safe + +#### 4. Image Handling Pipeline +1. Client: File → Base64 encoding +2. Server: Validation (type, size, content) +3. Server: Base64 → Gemini API format +4. Server: Generated image → Data URL +5. Client: Secure download with authentication check + +## Testing Guidelines + +When implementing new features, always write unit tests for: +- State reducer logic +- Validation functions +- API route handlers +- Custom hooks +- Utility functions + +Use Base UI components when building new UI features to maintain consistency. + +## Important Implementation Details + +### Environment Variables +Required in `.env.local`: +- `GEMINI_API_KEY`: Google Gemini API key (server-side only) +- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`: Clerk public key +- `CLERK_SECRET_KEY`: Clerk secret key + +### Security Considerations +- All file uploads are validated for type, size, and content +- API routes include rate limiting and error sanitization +- Object URLs are properly revoked to prevent memory leaks +- Download URLs are validated before execution + +### Component Patterns +- Use controlled components with dispatch actions +- Implement proper ARIA labels and keyboard navigation +- Follow the established pattern of separate step components (UploadStep, ConfigureStep, ResultsStep) +- Use dynamic imports for client-only components to avoid SSR issues + +### Type Safety Rules +- Never use `any` type +- All API responses must have defined types +- Component props must be explicitly typed +- Use discriminated unions for action types + +### Performance Optimizations +- Dynamic import for main app to bypass SSR +- Turbopack enabled for faster builds +- Image optimization via Next.js Image component +- Object URL cleanup on component unmount + +## Common Development Tasks + +### Adding a New Model Configuration Option +1. Update `ModelConfig` interface in `types.ts` +2. Add UI control in `ConfigPanel.tsx` +3. Update the prompt builder in `lib/gemini.ts` +4. Add validation if needed in `lib/validations.ts` + +### Creating a New API Endpoint +1. Create route file in `app/api/[endpoint]/route.ts` +2. Implement with proper error handling and validation +3. Never expose sensitive data or API keys +4. Add request/response types to `types.ts` + +### Implementing New UI Components +1. Create component in `components/` directory +2. Use Base UI components when possible +3. Include proper TypeScript props interface +4. Add ARIA labels and keyboard support +5. Follow existing Tailwind styling patterns \ No newline at end of file From 339309ef5bd28846a8ac75d2f849ea90082f7c35 Mon Sep 17 00:00:00 2001 From: benbaessler Date: Wed, 3 Sep 2025 02:53:43 +0200 Subject: [PATCH 02/30] feat: header component --- app/layout.tsx | 13 +++-------- components/Header.tsx | 50 ++++++++++++++++++++++++++++++++++++++++ public/logo-desktop.png | Bin 0 -> 3903 bytes public/logo-mobile.png | Bin 0 -> 7278 bytes 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 components/Header.tsx create mode 100644 public/logo-desktop.png create mode 100644 public/logo-mobile.png diff --git a/app/layout.tsx b/app/layout.tsx index 9ee7bc0..c6c566e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,10 +1,7 @@ import type { Metadata } from "next"; -import { - ClerkProvider, - SignedIn, - UserButton, -} from "@clerk/nextjs"; +import { ClerkProvider } from "@clerk/nextjs"; import { Geist, Geist_Mono } from "next/font/google"; +import { Header } from "@/components/Header"; import { Footer } from "@/components/Footer"; import "./globals.css"; @@ -35,11 +32,7 @@ export default function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} >
- -
- -
-
+
{children}
diff --git a/components/Header.tsx b/components/Header.tsx new file mode 100644 index 0000000..257f65b --- /dev/null +++ b/components/Header.tsx @@ -0,0 +1,50 @@ +'use client'; + +import Link from 'next/link'; +import { SignInButton, SignedIn, SignedOut, UserButton } from '@clerk/nextjs'; + +export function Header() { + return ( +
+
+
+ {/* Logo Section */} +
+ +
+ Mirror Studio +
+ +
+ + {/* Navigation Section */} +
+ + + + + + + + + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/public/logo-desktop.png b/public/logo-desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..d8747504742a783849a6af93f19b2543a1ffc1d8 GIT binary patch literal 3903 zcmc(iX*d*K`^QIAmW(yZ+9X-Bl&B_*82g?rW0#OU#*$&k9zu3ykHKUa`x1t-{g5p) z_9a_mX0neA!sGeB_+QWa=f!iK>ptf`-|ITx`?}9LZ$7bx`WnoPJd6MUfLTjZ)d&Ef z!Jg-C^q0&v>X*P1n-khymQL{~(p*Jq*(mnZ)kwyQ8{tDZ*n|D?1_`*QpEp@*# zf{$b+U35q>P86i6czzcJikV2MZz?XTB0t~0AcH@8tslfC3OJ1ORx+7|8+v ze7vwk3wTrVgnBNE{V!y!;tG}8+#0y&bQu5$yb&Dqrh9XxFKHf+Hx2Oit~m}030Y7K zJ#C|XbFy@>wK!jDkng|Lmn6tv(HCv#H5IqubjP1p5M$FmQ%+&6f;NSfOvTMR$y@GW z5Y-!Po{jv|%o2mi$Ml8z`UVEAA_jSMm=-ue|A0J+0se^-LcZbueU%4=q;A_&n$G=$ zhFHIBot_*Qbtnc?tL5e8$#!5?Y}}YevCGi4t%Y{-0v0*@)h29v1GI=mk_8kIvPaq9 zS!HzI?NEs0n}eCsxk;n#cNM3o;=M=BH7;h)g-|RQC4N)Kp+b6r1guChmo9=H5WWWtJ)> zzWq@-wKX!D_KCIT-_`EriLILJJ&6Ci>7*VKASR5Cbs`F_|NlQC96c<6?1H1aG|ynW zt8tYpXy$1F&NSd!4-P1cJdVz@PEmp;HCyB$tpvnHk-|GX3-R1PeJOhr>62I)ceg)^ zig53{-$XLc9(pa%VFFK%_HB8)NRU7?;#PI|x&=7tW&9`eB2{ z!4tOm5pN(NYyFcL+W^nimQNgmq3s&2K9r|N^;hh&VhTOVqstOSH;>5VYoharn8tdt zJS6H`Mr?ehG!Sd8ZIb9-Z%ep^fd*-&Ek<){u)-*@aw?rpYMUV4u1*J5b=mPW~70E!BAjc%Gpu7ZRzmH zh~DHTM8QA-xptjtrdlCGsV2Dbl1H;bv~<$`0^g;;46BY~Q7_J-n)drm zRmH#7a4y8q)(ro(_rww^y(_)gH*Wcz&X`Dh>VgLbo*J>>{Wht9n=6gYc7Q7&P|ujL=;aeHegpkm7bj)xDtg=#gw{>op7{N_3%1gE&X#oLv#x z#s$U|{k~+#H14P194Fi}uUVn;tG;yV8`3tX_Za-eZI|Rr&G2QA ze9uQwAiRq0-87BE!LT<;Rg2(!WjaE8cU0VDcv@6lk9mJaRQiRG#1PX#j@*Y8POc4} z@lRB_H9S~N2-B;fD11G(^vO>0-gk@Ad-9i)!J}xlmP5&ivm6ac@XokqLH$z+?>Fy@ zv@|I>9Al&Q+WTq;12j&*Lr$_u`05oM*fz9&r*#v8+K5mBed-z zJ+W$&1L|msg?l>k#U2yBTr3yR>mCegO?7|d<6EO}pK?V9azrxFzdre5g}1K%Afm@# z0VVWt3!9Z7?UX;~AE*1r(V7qxP14o@N)J`7z&$%pkb72MVpsPoaODHY3`DjZ|1Ei| zCc<1{X=kM~yr`mvDE~0mJupN44r%C*hR1N#%j>Z&l+6rg-Tu2QeWdV_6Dv^`38~G( zB%KcUT&%6+mhZ3b9U-qg9q2)G8uxu@_vTggY+<1=Z#&iLEEa>wci?|;@w6@Q? z6l*u$FGkut1q+yAAE78dt>0~mvs}AN$(4Cg=pq^v^nkiMf6|I*oFMcdmM8v7@8EdZ zcbj?twoW7mE@ppm?@Gwp2k!q)pX3>`Q16U+w9J(7r^2tope)gx9Jhrv%;wMzO$_)# zGF@-|fXFB_d#oeW#PanhIa=Jvb;%NPY0x))+q4%IDUBaeL_IZGar5g)tQ7V&FU_ww z+=|a{uz4w3;d?K4GV{mc@`AZcg%!X|)l&!j`b&;6Yo za1FmHsz*WL1t8;rz@>4$T&M@mX*BCL+$7jb(<@fpQpVR zBOQVRuhnL2Yr+!WYpfqjvSGlA1%1@3Q$kJkMZf9lP8gX0X}zo7ijhfpGAE%dCrE{M?#eBZl_# zjw3M&C0RhJt2vx1j@IHo`}$7D7bkaZtl1+m-mdK1OW#QMe0W00F>TS?E@tYl_(rfH^8^Q@Pqk-Q;{zXyM~aFY zQc_a>Dxz$IHN=-Z^>`uYU%oTuKTg+l`b_rgFIwg$IUSH9kE(-5!Q-Z`PyVP;`8>mF zvrm1=<*Tkz$9bpnT(%ouq?`7QSUQmo`b)=q1cN3{c#!q52ejh4`gys`%*-)2pSk`@ zp^}f8jAi$HG&o%zQ%IzN7u@dN1NT=e&1YI*wTaXpg|fP1T6-wRDk$3R!cv55wXC@7 zU*n+SjQPhUFF7;S0}F|xqxQWG5&>D`yaAdoVY=v7ddN79w}v7HB5`S~+I=Z*S112{ za(hGP(JjB$+k+;HDa+H;hfN zGu-?@3IEB9TsBhs#47DiL1{s;J@ba#m({l;&!oUY25!h}%lbD2q2bBdf;!S147m|7 znG_cM{dP>$&@daMj4>X7R;Jh60?&oj-@~zg$tbb~Hq864yCD-2U&Begm2+9w!(*BGNR2exvW4U~W5C@Hn^ky8Go_K?Yb z5`<#89_e{;P;7oS8CqZAm(o6<8VVJOX41(u$`jj+%MV|^YsO^>8mbZ!Z}or*=*>>S z)0*POtKFd^*(-GZ8Wnls`~(7_=zz57u&+vVj~%8an+dXFDbdO}@nBaw2^VS5Z3UDg zH-kkycl)!$f?d-t6jgk^!-2UlysD<)bvNDfYo+gR+!^s1L`s|?J7A=P2`+d2asRYt zCala@M+}LQ^R3j`lJucmTFQKLOb#CE)-a}Lsyqp#x3%dqEZ-hFQMORU)c?L||EwR! zYrvMQO~4J`i|8J-6U?yz*e_`HbUZS)`6TitNR{OUMcZNvHa^fgd;0?X%~ z>7WuTdEx8?zN^|En#LeY6H<*TT*27Cn6oc>N^HKirKGBfueWsd6tW8dPWM;rh=COy z80W+UjPX|$o$ny@DrR}Zx0Ugac2Xmh(a&<}OfI)%yk;5>ti;&It)mz83WHW0$5G4JRRU0bFFWh6xo0ZAf!Uyvq_ZL=!BRfq9iwB#>Ce9CK zBv;_-+eRJ9hs!wwZmOQmOD)0|iT-><4BmIGSyn34)Yd9E4YF2V zf;@1ME%p^vJgKM8%$wRxoH(bm36OX&;4aJ5|BTcAJ7S~?rY$#=%$ohoJm+12mYTk5 Jm5N>XzX0D~cxC_q literal 0 HcmV?d00001 diff --git a/public/logo-mobile.png b/public/logo-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..592216bd43c10513c8ff1b1c7362f590406e35e0 GIT binary patch literal 7278 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 Date: Wed, 3 Sep 2025 02:53:55 +0200 Subject: [PATCH 03/30] chore: remove unused file --- test-results.md | 121 ------------------------------------------------ 1 file changed, 121 deletions(-) delete mode 100644 test-results.md diff --git a/test-results.md b/test-results.md deleted file mode 100644 index d5378d5..0000000 --- a/test-results.md +++ /dev/null @@ -1,121 +0,0 @@ -# Clerk Authentication Fix Results - -## ✅ Issues Resolved - -### 1. Content Security Policy (CSP) Compatibility -**Before**: CSP was blocking Clerk JavaScript from loading -**After**: Enhanced CSP with all required Clerk domains and directives - -### 2. Missing CSP Directives -**Before**: Missing `child-src`, `worker-src`, `form-action` directives -**After**: Added all required directives for Clerk functionality - -### 3. Error Handling and Debugging -**Before**: No error handling or debugging for Clerk issues -**After**: Added comprehensive debugging utilities and error handling - -## 🔧 Technical Changes Made - -### 1. Enhanced CSP Configuration (`middleware.ts`) -```diff -+ // Content Security Policy - Enhanced for Clerk compatibility -+ const clerkDomains = [ -+ 'https://clerk.com', -+ 'https://*.clerk.com', -+ 'https://api.clerk.com', -+ 'https://*.clerk.dev', -+ 'https://accounts.dev', -+ 'https://*.accounts.dev' -+ ]; - -+ script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomains} -+ child-src 'self' ${clerkDomains} -+ worker-src 'self' blob: -+ form-action 'self' ${clerkDomains} -+ frame-src 'self' ${clerkDomains} -+ connect-src 'self' ${clerkDomains} wss://ws.clerk.com -``` - -### 2. CSP Development Mode (`middleware.ts`) -```diff -+ // Add CSP violation reporting in development -+ if (!isProduction) { -+ response.headers.set('Content-Security-Policy-Report-Only', csp) -+ console.log('CSP Policy (Report-Only):', csp) -+ } else { -+ response.headers.set('Content-Security-Policy', csp) -+ } -``` - -### 3. Debugging Utilities (`utils/clerk-debug.ts`) -- Added comprehensive Clerk debugging class -- CSP violation monitoring -- Environment validation -- Clerk initialization testing -- Automatic debugging in development mode - -### 4. Enhanced Error Handling (`MirrorStudioApp.tsx`) -```diff -+ const { userId, isLoaded, isSignedIn } = useAuth(); -+ -+ // Check if Clerk is loaded first -+ if (!isLoaded) { -+ clerkDebugger.logWarning('Clerk not yet loaded when download attempted'); -+ return; -+ } -+ -+ try { -+ clerkDebugger.logInfo('Opening sign-in modal'); -+ openSignIn(); -+ } catch (error) { -+ clerkDebugger.logError('Failed to open sign-in modal', error); -+ console.error('Sign-in error:', error); -+ } -``` - -## 🧪 Testing Results - -### Environment Variables ✅ -- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`: Present and valid format -- `CLERK_SECRET_KEY`: Present and valid format - -### CSP Headers ✅ -- All Clerk domains included in relevant directives -- Report-Only mode in development for debugging -- Production mode will enforce CSP - -### Clerk Connectivity ✅ -- Main Clerk domains accessible -- CSP allows all required Clerk endpoints -- WebSocket connections permitted for real-time features - -## 🚀 Expected Resolution - -The following issues should now be resolved: - -1. **"Error: Clerk: Failed to load Clerk"** - CSP now permits Clerk JavaScript -2. **Sign-in dialog not opening** - Enhanced error handling and CSP compatibility -3. **Authentication hooks failing** - Proper loading state checking added - -## 🔍 How to Verify - -1. **Open Browser Console**: Navigate to `http://localhost:3004/` -2. **Check for Errors**: Should no longer see "Failed to load Clerk" error -3. **Test Sign-In**: Click "Sign in to Download" button - dialog should open -4. **Debug Info**: Look for Clerk debug messages in console (development only) - -## 📋 Next Steps - -1. **Test in Browser**: Verify sign-in dialog opens correctly -2. **Complete Authentication Flow**: Test full sign-up/sign-in process -3. **Production Testing**: Deploy and test with production CSP mode -4. **Monitor CSP Violations**: Check for any remaining CSP issues - -## 🔒 Security Notes - -- CSP remains restrictive while supporting Clerk -- All Clerk domains are official and trusted -- Report-Only mode in development prevents blocking during testing -- Production mode enforces full CSP protection - -The Clerk authentication issue should now be fully resolved with enhanced security and debugging capabilities. \ No newline at end of file From 466615eb1205b724b68c6a5ad087d04e37a925aa Mon Sep 17 00:00:00 2001 From: benbaessler Date: Wed, 3 Sep 2025 03:44:35 +0200 Subject: [PATCH 04/30] style: apply new color scheme --- CLAUDE.md | 9 ++++++++- app/MirrorStudioApp.tsx | 34 +++++++++++++++++++-------------- app/globals.css | 34 +++++++++++++++++++++++---------- app/layout.tsx | 2 +- components/Footer.tsx | 6 +++--- components/Header.tsx | 42 +++++++++++++++++++++++++++++++---------- 6 files changed, 88 insertions(+), 39 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1d6ca53..e0d7b43 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -156,4 +156,11 @@ Required in `.env.local`: 2. Use Base UI components when possible 3. Include proper TypeScript props interface 4. Add ARIA labels and keyboard support -5. Follow existing Tailwind styling patterns \ No newline at end of file +5. Follow existing Tailwind styling patterns +- use this as the color scheme throughout the app: #1A1A1A – Charcoal black (main text / background) + +#EAEAEA – Light grey (section backgrounds) + +#C1A57B – Warm beige (accent for buttons / highlights) + +#FFFFFF – White (primary contrast) \ No newline at end of file diff --git a/app/MirrorStudioApp.tsx b/app/MirrorStudioApp.tsx index 005c7ce..ac19bfb 100644 --- a/app/MirrorStudioApp.tsx +++ b/app/MirrorStudioApp.tsx @@ -50,10 +50,10 @@ interface ResultsStepProps { const UploadStep = ({ clothingItems, dispatch }: UploadStepProps) => (
-

+

what do you want to showcase?

-

+

Upload images of clothing items you want your model to wear.

@@ -68,7 +68,13 @@ const UploadStep = ({ clothingItems, dispatch }: UploadStepProps) => ( dispatch({ type: 'SET_STEP', payload: 'configure' }); } }} - className="inline-flex items-center justify-center gap-2 px-8 py-3 text-base font-bold text-white bg-indigo-600 rounded-lg shadow-md transition-all duration-200 transform hover:bg-indigo-700 focus:outline-none focus:ring-4 focus:ring-indigo-300 focus:ring-offset-2" + className="inline-flex items-center justify-center gap-2 px-8 py-2 text-sm font-medium text-[var(--color-charcoal)] rounded-full shadow-lg hover:shadow-xl focus:outline-none transition duration-200 hover:brightness-95" + style={{ + background: + 'linear-gradient(to bottom, var(--color-light-grey), color-mix(in srgb, var(--color-light-grey) 85%, var(--color-white)))', + border: + '1px solid color-mix(in srgb, var(--color-light-grey) 70%, var(--color-charcoal))', + }} aria-label="Proceed to model configuration step" > Next @@ -83,7 +89,7 @@ const ConfigureStep = ({ state, dispatch, onGenerate }: ConfigureStepProps) => { return (
-

+

how do you want your model to look?

@@ -99,7 +105,7 @@ const ConfigureStep = ({ state, dispatch, onGenerate }: ConfigureStepProps) => { dispatch({ type: 'SET_STEP', payload: 'upload' }); } }} - className="inline-flex items-center justify-center gap-2 px-8 py-3 text-base font-semibold text-slate-700 bg-slate-200 rounded-lg shadow-md transition-all duration-200 transform hover:bg-slate-300 focus:outline-none focus:ring-4 focus:ring-slate-400 focus:ring-offset-2" + className="inline-flex items-center justify-center gap-2 px-8 py-3 text-base font-semibold text-[var(--color-charcoal)] bg-[color-mix(in_srgb,var(--color-light-grey)_80%,var(--color-white))] rounded-lg shadow-md transition-all duration-200 transform hover:bg-[color-mix(in_srgb,var(--color-light-grey)_70%,var(--color-white))] focus:outline-none focus:ring-4 focus:ring-[color-mix(in_srgb,var(--color-warm-beige)_40%,transparent)] focus:ring-offset-2" aria-label="Go back to clothing items upload step" >