diff --git a/.cursor/debug-f6a22a.log b/.cursor/debug-f6a22a.log new file mode 100644 index 0000000..abbb7c2 --- /dev/null +++ b/.cursor/debug-f6a22a.log @@ -0,0 +1,8 @@ +{"sessionId":"f6a22a","runId":"pre-fix","hypothesisId":"A","location":"TemplateCard.tsx:UseTemplateClick","message":"Use Template button clicked","data":{"templateId":"tpl-video-1","name":"Promo Reel 2","category":"Video","hasOnUseTemplateProp":false},"timestamp":1779381720022} +{"sessionId":"f6a22a","runId":"pre-fix","hypothesisId":"A","location":"TemplateCard.tsx:UseTemplateClick","message":"Use Template button clicked","data":{"templateId":"tpl-video-1","name":"Promo Reel 2","category":"Video","hasOnUseTemplateProp":false},"timestamp":1779381720512} +{"sessionId":"f6a22a","runId":"pre-fix","hypothesisId":"A","location":"TemplateCard.tsx:UseTemplateClick","message":"Use Template button clicked","data":{"templateId":"tpl-video-1","name":"Promo Reel 2","category":"Video","hasOnUseTemplateProp":false},"timestamp":1779381720934} +{"sessionId":"f6a22a","runId":"pre-fix","hypothesisId":"A","location":"TemplateCard.tsx:UseTemplateClick","message":"Use Template button clicked","data":{"templateId":"tpl-video-1","name":"Promo Reel 2","category":"Video","hasOnUseTemplateProp":false},"timestamp":1779381721478} +{"sessionId":"f6a22a","runId":"post-fix","hypothesisId":"A","location":"TemplateCard.tsx:UseTemplateClick","message":"Use Template button clicked","data":{"templateId":"tpl-video-0","name":"Promo Reel","category":"Video","hasOnUseTemplateHandler":true,"isUsingTemplate":false},"timestamp":1779381843823} +{"sessionId":"f6a22a","runId":"post-fix","hypothesisId":"E","location":"TemplatesPageContent.tsx:handleUseTemplate","message":"Creating project from template","data":{"templateId":"tpl-video-0","category":"Video"},"timestamp":1779381843823} +{"sessionId":"f6a22a","runId":"post-fix","hypothesisId":"A","location":"TemplateCard.tsx:UseTemplateClick","message":"Use Template button clicked","data":{"templateId":"tpl-video-1","name":"Promo Reel 2","category":"Video","hasOnUseTemplateHandler":true,"isUsingTemplate":false},"timestamp":1779381847288} +{"sessionId":"f6a22a","runId":"post-fix","hypothesisId":"E","location":"TemplatesPageContent.tsx:handleUseTemplate","message":"Creating project from template","data":{"templateId":"tpl-video-1","category":"Video"},"timestamp":1779381847289} diff --git a/.cursorrules b/.cursorrules index 175e522..e774a74 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,6 +1,15 @@ -# Project: CreatorStudio +# Project: FlatRender # A Renderforest-style platform with Video Maker and Image Maker products. +## 🧠 Project Memory — READ THIS FIRST +# Before starting ANY task, read PROJECT_MEMORY.md at the project root. +# It contains: completed features, backlog, known bugs, architecture decisions, +# environment variable status, and the full file map. +# After completing a task, update PROJECT_MEMORY.md: +# - Move item from Backlog → Completed +# - Add any new bugs to Known Issues +# - Add a row to the Session Log with today's date and what changed + ## Stack - Next.js 14 App Router (never use pages/ directory) - TypeScript (strict mode, no `any`) @@ -55,4 +64,16 @@ - Never install axios — use native fetch - Never add console.log to committed code - Never create a component larger than 150 lines — split it -- Never skip TypeScript types on props — always define an interface \ No newline at end of file +- Never skip TypeScript types on props — always define an interface + +## Studio Modules +- Canvas editor uses React-Konva (never plain HTML5 canvas API) +- All canvas state lives in Zustand studio-store.ts +- ffmpeg.wasm runs in a Web Worker only (never on main thread) +- Studio pages are "use client" — they are 100% client components +- Timeline px-per-second default: 60 (1 second = 60px wide) +- Konva Stage base size: 1280x720 (scaled to container) +- Image editor and Video Studio share the same Layer type interface +- Server render API uses nexrender for After Effects pipeline +- Never block UI during ffmpeg processing — always show progress +- All studio panels are dark theme (bg-gray-900, text-white) \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1eed665 --- /dev/null +++ b/.env.example @@ -0,0 +1,35 @@ +# Image editor — background removal (https://www.remove.bg/api) +REMOVE_BG_API_KEY= +# Optional self-hosted rembg HTTP endpoint (POST raw image bytes → PNG) +REMBG_SERVICE_URL= + +# Video render pipeline +RENDER_WORKER_URL=http://localhost:3355 +RENDER_WORKER_SECRET= +RENDER_WORKER_PORT=3355 +RENDER_MOCK=true +NEXRENDER_TEMPLATE_SRC=file:///path/to/template.aep +NEXRENDER_COMPOSITION=CreatorStudio_Main +NEXRENDER_BINARY= +NEXRENDER_SERVER_URL= +NEXRENDER_WORKPATH=.nexrender + +# Site URL for SEO metadata, OAuth, and Stripe redirects +NEXT_PUBLIC_SITE_URL=http://localhost:3000 + +# Supabase — required for auth, dashboard, and saving projects (templates → studio) +# https://supabase.com/dashboard/project/_/settings/api +# Without these, `npm run dev` still opens the studio via a local mock project ID (dev only). +NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key +SUPABASE_SERVICE_ROLE_KEY=your-service-role-key + +# Stripe — https://dashboard.stripe.com/apikeys +STRIPE_SECRET_KEY=sk_test_... +STRIPE_WEBHOOK_SECRET=whsec_... + +# Stripe Price IDs (Products → each price → copy ID) +STRIPE_PRICE_PRO_MONTHLY=price_... +STRIPE_PRICE_PRO_ANNUAL=price_... +STRIPE_PRICE_BUSINESS_MONTHLY=price_... +STRIPE_PRICE_BUSINESS_ANNUAL=price_... diff --git a/PROJECT_MEMORY.md b/PROJECT_MEMORY.md new file mode 100644 index 0000000..45ccec2 --- /dev/null +++ b/PROJECT_MEMORY.md @@ -0,0 +1,358 @@ +# FlatRender — Project Memory +> **Rule:** Read this file before starting any task. Update it after every completed feature, bug fix, or architectural decision. +> Both Cursor AI and Claude Code use this as the project brain. + +--- + +## 📌 Project Identity + +| Key | Value | +|---|---| +| Project folder | `D:\Projects\flatrender` | +| Brand name | **FlatRender** (package: `flatrender`) — ✅ All UI updated to FlatRender | +| Products | Video Maker · Image Maker | +| Stack | Next.js 14 App Router · TypeScript · Tailwind CSS · shadcn/ui · Framer Motion | +| Canvas | React-Konva (Konva.js) — both Video Studio and Image Editor | +| State | Zustand (`studio-store.ts`, `image-editor-store.ts`) | +| Auth + DB | Supabase (`@supabase/ssr`) | +| Payments | Stripe | +| Video (browser) | ffmpeg.wasm in Web Worker (`src/workers/ffmpeg-trim.worker.ts`) | +| Video (server) | nexrender + Adobe After Effects (`server/render-worker.ts`) | +| Dev server | `npm run dev` → http://localhost:3000 | +| Render worker | `npm run render-worker` → http://localhost:3355 | + +--- + +## ✅ Completed Features + +### Landing Page (`/`) +- [x] `Hero` — gradient on “AI” (`from-blue-600 via-violet-500 to-blue-500`), dual CTA, preview cards, blobs +- [x] `HeroPreviewCards` — Mixkit MP4 loops, play overlay fades on hover, Framer Motion stagger +- [x] `ProductsShowcase` — Video Maker + Image Maker cards with glassmorphism style +- [x] `TemplateGallery` — filter tabs, 8-card grid, `scroll-mt-20` for sticky nav anchor +- [x] `TemplateCard` — hover Mixkit video via `previewVideoUrl`, bottom "Use Template" CTA, `AnimatePresence` fade +- [x] `template-gallery-data.ts` — `previewVideoUrl` on video/social template entries +- [x] `HowItWorks` — 3-step process, alternating layout, scroll-triggered animations +- [x] `Pricing` — monthly/annual toggle, green “Save 20%” yearly badge, 3 tiers, Stripe checkout wired +- [x] `PricingCompareTable` — full feature comparison table, 5 sections, synced billing toggle, Pro column highlight +- [x] `Testimonials` — 6-card grid +- [x] `FAQ` — accordion, 2-column layout +- [x] `Navbar` — FlatRender logo, Video/Image Maker + Learn dropdowns (shadcn), Pricing link, Sign In / Try for Free; mobile sheet (`navbar-menu-data.ts`) +- [x] `Footer` — 4-column, dark background + +### Product Pages +- [x] `/video-maker` — Hero, Features, UseCases, TemplateCarousel, CTA +- [x] `/image-maker` — Hero, BeforeAfter, Gallery, Features, UseCases, CTA + +### Templates Page (`/templates`) +- [x] Renderforest layout: 260px category sidebar + carousel rows (`VideoTemplatesCategorySidebar`, `VideoTemplatesCarouselRow`) +- [x] Toolbar: search, Premium Only (Switch), All Sizes select (16:9 / 9:16 / 1:1 / 4:5), Sort by (local state) +- [x] Sidebar filters panel (collapsed): Premium + size; `?category=` from navbar +- [x] `VideoTemplatesPageContent` — client-side filtering via `video-templates-catalog.ts` +- [x] Template detail `/templates/[id]` — `TemplateDetailContent` (preview, styles, Create Now, examples row); `generateStaticParams` from catalog + +### Auth (`/auth`) +- [x] Sign In / Sign Up tabs +- [x] Email + password (react-hook-form + zod) +- [x] Google OAuth button +- [x] Supabase auth integration +- [x] OAuth callback route (`/auth/callback`) +- [x] Sign-out route (`/auth/sign-out`) +- [x] `SupabaseSetupNotice` — shown when env vars missing (dev-friendly) + +### Dashboard (`/dashboard`) +- [x] `DashboardShell` — layout wrapper +- [x] `DashboardSidebar` — logo, nav links, user avatar + plan badge +- [x] `DashboardTopBar` — search + "New Project" dropdown +- [x] `NewProjectMenu` — Video / Image / Trimmer options +- [x] `DashboardProjectsSection` — projects grid from Supabase; `isLoading` shows 6-card skeleton grid +- [x] `DashboardProjectsContent` — async Supabase fetch (Suspense on `/dashboard`) +- [x] `SkeletonProjectCard` — pulse placeholders matching `ProjectCard` layout +- [x] `DashboardPlanBadge` — async plan fetch; sidebar `Suspense` + `DashboardPlanBadgeSkeleton` +- [x] `DashboardSidebarNav` — client nav (pathname-aware) +- [x] `DashboardEmptyState` — illustration + CTA +- [x] `ProjectCard` — thumbnail, type badge, status, 3-dot menu +- [x] `/dashboard/settings` — settings page + +### Video Creation Studio (`/studio/video/[projectId]`) +- [x] `VideoStudioLayout` — icon dock (56px) + fixed 220px tool panel + full-width canvas/timeline (no right `PropertiesPanel`); `StudioMobileGate` below 768px; `useStudioProjectPersistence` (3s debounced save; dev 404 → `localStorage` `flatrender-project-{id}`) +- [x] `StudioSidebarDock` — Audio / TTS / Colors / Transitions / Font / Watermark + Guide + Keyboard (toasts); blue active bar; scenes via timeline strip only +- [x] `WatermarkSidebarContent` — upload placeholder, 3×3 position grid, opacity slider +- [x] Sidebar panels — `AudioSidebarContent`, `ColorsSidebarContent`, `TransitionsSidebarContent` (Random / No Transition tiles, apply all scenes), `FontSidebarContent`, `WatermarkSidebarContent` +- [x] `scene-browser-data.ts`, `SceneBrowserCard`, shadcn `Tabs` for media filter +- [x] `/studio/video/new` — Renderforest-style onboarding (Select Scenes / AI / presets) before editor +- [x] `VideoProjectNewContent`, `TEMPLATE_GALLERY_ITEMS` (picsum thumbnails); preset click → `/templates/[id]`; catalog includes onboarding preset ids +- [x] `SceneBrowserModal` — full-screen library (categories, Video/Photo tabs, search, 28 scenes); onboarding + studio “Browse Scenes” +- [x] `StudioMobileGate` + `useIsMobile` — desktop-only gate for video/image studio (`matchMedia` max-width 767px) +- [x] `ResizableStudioPanel` — drag-to-resize left/right panels +- [x] `StudioTopBar` — breadcrumb (My Projects → name), `StudioTopBarSaveBadge` (Local / Saved ✓ / dot), centered undo/redo + toolbar, `StudioTopBarTextControls` when text layer selected, Export dropdown → `RenderModal` presets +- [x] `PropertiesPanel` — still used by image editor; not mounted in video studio layout +- [x] `dev-project-storage.ts` — dev-only localStorage fallback when Supabase returns 404 +- [x] `render-presets.ts` — full / 720p preview / GIF export presets for `RenderModal` +- [x] Scenes managed via timeline `SceneThumbnailStrip` only (no left sidebar scenes panel): 120×80px blocks, rename, browse (`SceneBrowserModal`), duplicate/delete on hover +- [x] `SceneTransitionPicker` — None / Fade / Slide / Zoom popover on outgoing scene +- [x] `scene-transitions.ts` — Framer Motion `animate()` playback (300ms fade, slide-left, zoom) +- [x] `DraggableSceneItem` + `SceneItemActions` — live Konva `thumbnailUrl` previews +- [x] `AddSceneMenu` — blank / from template +- [x] `CanvasEditor` — React-Konva Stage (1280×720), scaled to container + - [x] Text layers (draggable, resizable, rotatable) + - [x] Image layers + - [x] Video clip layers + - [x] Shape layers (rect, circle, line, arrow) + - [x] Transformer (resize handles, rotation, min 8px guard) + - [x] Click empty area → deselect + - [x] Circle drag fix (center-origin correction) +- [x] `CanvasLayerNode` router → `TextLayerNode`, `ImageLayerNode`, `ShapeLayerNode`, `VideoLayerNode` +- [x] `PropertiesPanel` — context-sensitive to selected layer type + - [x] `TextLayerProperties` — font, size, bold/italic, color, align, letter-spacing, line-height, animation style + - [x] `ImageLayerProperties` — opacity, flip H/V, replace, border-radius + - [x] `ShapeLayerProperties` — fill, stroke, stroke-width, border-radius + - [x] `CommonLayerControls` — X/Y/W/H, rotation, z-order, delete + - [x] `PropertyControls` + `useLayerUpdater` +- [x] `Timeline` — 180px Renderforest layout: `TimelineControlBar`, `SceneThumbnailStrip` / `SceneThumbnailBlock`, `TimelineActionRow`; playhead on strip (`STRIP_PX_PER_SECOND` 24); zoom slider 30–120 + - [x] `TimeRuler` — time markers, click to seek + - [x] `SceneTrack` + `SceneBlock` — color-coded, drag-right-edge to resize duration + - [x] `AudioTrack` — file picker, file name display + - [x] `TimelinePlayhead` — red playhead overlay on thumbnail strip during playback + - [x] Legacy `SceneBlock` / `SceneTrack` / `TimeRuler` / `AudioTrack` — unused by Timeline (kept for reference) +- [x] `StudioToolbar` — add Text / Image / Video / Shape buttons +- [x] `RenderModal` — resolution, FPS, progress bar, download link +- [x] `useCanvasKeyboard` — Delete, Ctrl+C/V/Z/Y +- [x] `useCanvasPreviewPlayback` — scene-by-scene playback with timing +- [x] `useContainerSize` — responsive canvas scaling +- [x] `studio-store.ts` — full Zustand store (scenes, layers, playback, zoom, audio, undo/redo) +- [x] `studio-history.ts` — past/future snapshot undo stack (limit 50) +- [x] `studio-timeline.ts` — duration helpers, zoom levels, scene-at-time +- [x] `studio-types.ts` — Scene, Layer, LayerType, `SceneTransition`, AddLayerInput interfaces +- [x] `studio-snapshot.ts` — Konva stage → PNG download +- [x] `studio-canvas-stage.ts` — global stage ref registry +- [x] `studio-scene-thumbnail.ts` — `toDataURL({ pixelRatio: 0.2 })` + deferred capture after layer edits +- [x] `studio-scene-data.ts` / `image-scene-data.ts` — parse + serialize `scene_data` for persistence +- [x] `ProjectSaveIndicator` — Saving… / Saved / Local save / Save failed (image editor); video studio uses `StudioTopBarSaveBadge` +- [x] `canvas-transform.ts` — node transform → layer coords +- [x] `studio-layer-props.ts` — typed prop accessors per layer type +- [x] `dev-mock-project.ts` — dev-only mock for testing without Supabase + +### Video Trimmer (`/studio/trimmer`) +- [x] `TrimmerUploadZone` — drag-drop + click, accepts video/* +- [x] `TrimmerVideoPreview` — `