21 KiB
21 KiB
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 (/)
Hero— gradient on “AI” (from-blue-600 via-violet-500 to-blue-500), dual CTA, preview cards, blobsHeroPreviewCards— Mixkit MP4 loops, play overlay fades on hover, Framer Motion staggerProductsShowcase— Video Maker + Image Maker cards with glassmorphism styleTemplateGallery— filter tabs, 8-card grid,scroll-mt-20for sticky nav anchorTemplateCard— hover Mixkit video viapreviewVideoUrl, bottom "Use Template" CTA,AnimatePresencefadetemplate-gallery-data.ts—previewVideoUrlon video/social template entriesHowItWorks— 3-step process, alternating layout, scroll-triggered animationsPricing— monthly/annual toggle, green “Save 20%” yearly badge, 3 tiers, Stripe checkout wiredPricingCompareTable— full feature comparison table, 5 sections, synced billing toggle, Pro column highlightTestimonials— 6-card gridFAQ— accordion, 2-column layoutNavbar— FlatRender logo, Video/Image Maker + Learn dropdowns (shadcn), Pricing link, Sign In / Try for Free; mobile sheet (navbar-menu-data.ts)Footer— 4-column, dark background
Product Pages
/video-maker— Hero, Features, UseCases, TemplateCarousel, CTA/image-maker— Hero, BeforeAfter, Gallery, Features, UseCases, CTA
Templates Page (/templates)
- Renderforest layout: 260px category sidebar + carousel rows (
VideoTemplatesCategorySidebar,VideoTemplatesCarouselRow) - Toolbar: search, Premium Only (Switch), All Sizes select (16:9 / 9:16 / 1:1 / 4:5), Sort by (local state)
- Sidebar filters panel (collapsed): Premium + size;
?category=from navbar VideoTemplatesPageContent— client-side filtering viavideo-templates-catalog.ts- Template detail
/templates/[id]—TemplateDetailContent(preview, styles, Create Now, examples row);generateStaticParamsfrom catalog
Auth (/auth)
- Sign In / Sign Up tabs
- Email + password (react-hook-form + zod)
- Google OAuth button
- Supabase auth integration
- OAuth callback route (
/auth/callback) - Sign-out route (
/auth/sign-out) SupabaseSetupNotice— shown when env vars missing (dev-friendly)
Dashboard (/dashboard)
DashboardShell— layout wrapperDashboardSidebar— logo, nav links, user avatar + plan badgeDashboardTopBar— search + "New Project" dropdownNewProjectMenu— Video / Image / Trimmer optionsDashboardProjectsSection— projects grid from Supabase;isLoadingshows 6-card skeleton gridDashboardProjectsContent— async Supabase fetch (Suspense on/dashboard)SkeletonProjectCard— pulse placeholders matchingProjectCardlayoutDashboardPlanBadge— async plan fetch; sidebarSuspense+DashboardPlanBadgeSkeletonDashboardSidebarNav— client nav (pathname-aware)DashboardEmptyState— illustration + CTAProjectCard— thumbnail, type badge, status, 3-dot menu/dashboard/settings— settings page
Video Creation Studio (/studio/video/[projectId])
VideoStudioLayout— icon dock (56px) + fixed 220px tool panel + full-width canvas/timeline (no rightPropertiesPanel);StudioMobileGatebelow 768px;useStudioProjectPersistence(3s debounced save; dev 404 →localStorageflatrender-project-{id})StudioSidebarDock— Audio / TTS / Colors / Transitions / Font / Watermark + Guide + Keyboard (toasts); blue active bar; scenes via timeline strip onlyWatermarkSidebarContent— upload placeholder, 3×3 position grid, opacity slider- Sidebar panels —
AudioSidebarContent,ColorsSidebarContent,TransitionsSidebarContent(Random / No Transition tiles, apply all scenes),FontSidebarContent,WatermarkSidebarContent scene-browser-data.ts,SceneBrowserCard, shadcnTabsfor media filter/studio/video/new— Renderforest-style onboarding (Select Scenes / AI / presets) before editorVideoProjectNewContent,TEMPLATE_GALLERY_ITEMS(picsum thumbnails); preset click →/templates/[id]; catalog includes onboarding preset idsSceneBrowserModal— full-screen library (categories, Video/Photo tabs, search, 28 scenes); onboarding + studio “Browse Scenes”StudioMobileGate+useIsMobile— desktop-only gate for video/image studio (matchMediamax-width 767px)ResizableStudioPanel— drag-to-resize left/right panelsStudioTopBar— breadcrumb (My Projects → name),StudioTopBarSaveBadge(Local / Saved ✓ / dot), centered undo/redo + toolbar,StudioTopBarTextControlswhen text layer selected, Export dropdown →RenderModalpresetsPropertiesPanel— still used by image editor; not mounted in video studio layoutdev-project-storage.ts— dev-only localStorage fallback when Supabase returns 404render-presets.ts— full / 720p preview / GIF export presets forRenderModal- Scenes managed via timeline
SceneThumbnailStriponly (no left sidebar scenes panel): 120×80px blocks, rename, browse (SceneBrowserModal), duplicate/delete on hover SceneTransitionPicker— None / Fade / Slide / Zoom popover on outgoing scenescene-transitions.ts— Framer Motionanimate()playback (300ms fade, slide-left, zoom)DraggableSceneItem+SceneItemActions— live KonvathumbnailUrlpreviewsAddSceneMenu— blank / from templateCanvasEditor— React-Konva Stage (1280×720), scaled to container- Text layers (draggable, resizable, rotatable)
- Image layers
- Video clip layers
- Shape layers (rect, circle, line, arrow)
- Transformer (resize handles, rotation, min 8px guard)
- Click empty area → deselect
- Circle drag fix (center-origin correction)
CanvasLayerNoderouter →TextLayerNode,ImageLayerNode,ShapeLayerNode,VideoLayerNodePropertiesPanel— context-sensitive to selected layer typeTextLayerProperties— font, size, bold/italic, color, align, letter-spacing, line-height, animation styleImageLayerProperties— opacity, flip H/V, replace, border-radiusShapeLayerProperties— fill, stroke, stroke-width, border-radiusCommonLayerControls— X/Y/W/H, rotation, z-order, deletePropertyControls+useLayerUpdater
Timeline— 180px Renderforest layout:TimelineControlBar,SceneThumbnailStrip/SceneThumbnailBlock,TimelineActionRow; playhead on strip (STRIP_PX_PER_SECOND24); zoom slider 30–120TimeRuler— time markers, click to seekSceneTrack+SceneBlock— color-coded, drag-right-edge to resize durationAudioTrack— file picker, file name displayTimelinePlayhead— red playhead overlay on thumbnail strip during playback- Legacy
SceneBlock/SceneTrack/TimeRuler/AudioTrack— unused by Timeline (kept for reference)
StudioToolbar— add Text / Image / Video / Shape buttonsRenderModal— resolution, FPS, progress bar, download linkuseCanvasKeyboard— Delete, Ctrl+C/V/Z/YuseCanvasPreviewPlayback— scene-by-scene playback with timinguseContainerSize— responsive canvas scalingstudio-store.ts— full Zustand store (scenes, layers, playback, zoom, audio, undo/redo)studio-history.ts— past/future snapshot undo stack (limit 50)studio-timeline.ts— duration helpers, zoom levels, scene-at-timestudio-types.ts— Scene, Layer, LayerType,SceneTransition, AddLayerInput interfacesstudio-snapshot.ts— Konva stage → PNG downloadstudio-canvas-stage.ts— global stage ref registrystudio-scene-thumbnail.ts—toDataURL({ pixelRatio: 0.2 })+ deferred capture after layer editsstudio-scene-data.ts/image-scene-data.ts— parse + serializescene_datafor persistenceProjectSaveIndicator— Saving… / Saved / Local save / Save failed (image editor); video studio usesStudioTopBarSaveBadgecanvas-transform.ts— node transform → layer coordsstudio-layer-props.ts— typed prop accessors per layer typedev-mock-project.ts— dev-only mock for testing without Supabase
Video Trimmer (/studio/trimmer)
TrimmerUploadZone— drag-drop + click, accepts video/*TrimmerVideoPreview—<video>+react-rndcrop overlay, aspect ratio buttonsTrimmerStrip— frame thumbnail strip, draggable trim handlesTrimmerExportSection— MP4/WebM toggle, export button, progress, downloadffmpeg-trim.worker.ts— full ffmpeg.wasm Web Worker (init, progress, process, complete)ffmpeg-worker-client.ts— typed client to communicate with workertrimmer-types.ts+trimmer-utils.ts— types and crop scaling math
Image Editor (/studio/image and /studio/image/[projectId])
ImageEditorLayout— full-viewport, dark theme; mobile gate;useImageProjectPersistenceauto-saveImageEditorTopBar— project name, export buttonImageEditorToolbar— Select, Crop, Text, Shape, Draw, AI toolsImageEditorCanvas— dynamic import (SSR off), Konva stageImageBaseLayer— base image rendering with Konva filtersImageEditorLayerNode— text/shape/draw layer nodesImageCropOverlay—react-rndcrop box on canvas (aspect lock)VignetteOverlay— radial gradient vignette
ImageCropControls— aspect presets (Free, 1:1, 16:9, 4:3, 9:16), Apply/Cancel above canvasimage-editor-crop.ts— aspect math, canvas→source crop,cropImageDataUrlImageEditorRightPanel— tabbed: Adjust | Filters | LayersAdjustPanel— brightness, contrast, saturation, hue, blur, sharpen, vignette slidersFiltersPanel— 12 preset filter thumbnailsLayersPanel— reorder, hide, lock, delete
AiRemoveBgModal— calls/api/remove-bg, shows resultimage-editor-store.ts— Zustand store for image editorimage-editor-filters.ts— Konva filter pipeline helpersimage-editor-konva.ts— Konva helpersimage-editor-types.ts— ImageLayer, ImageEditorState typesimage-editor-export.ts— stage → PNG/JPG/WebP downloadimage-editor-stage-ref.ts— global stage refimage-editor-transform.ts— transform helpers
API Routes
POST /api/checkout— creates Stripe Checkout sessionPOST /api/webhooks/stripe— updates user plan on checkout.session.completedGET/POST /api/projects— fetch / create user projectsGET/PATCH /api/projects/[projectId]— load project + auto-savescene_dataPOST /api/remove-bg— calls remove.bg or rembg servicePOST /api/render— queues render job in SupabaseGET /api/render/[jobId]/status— poll render job status
Server (render worker process)
server/render-worker.ts— HTTP server on port 3355,/health+/processserver/render-job-processor.ts— fetches job from Supabase, runs nexrender, uploads resultserver/nexrender-job-builder.ts— builds nexrender job JSON from scene dataserver/nexrender.d.ts— type declarations for @nexrender/core
Infrastructure
supabase/migrations/001_profiles.sql— profiles table, RLSsupabase/migrations/002_render_jobs.sql— render_jobs table, RLSsupabase/migrations/003_projects.sql— projects table, RLS, updated_at trigger.env.example— all required env vars documentednext.config.mjs— webpack globalObject fix + COOP/COEP headers (required for ffmpeg.wasm).cursorrules— full project rules for Cursor AItailwind.config.ts— custom colors, font familiescomponents.json— shadcn/ui config
🔄 In Progress
Nothing currently in progress.
Landing page status (2026-05-21 polish)
npx tsc --noEmit— clean (no TypeScript errors)- Tailwind
rf.blue/rf.blue-light—#2563EB/#EFF6FF - Remaining pre-launch work is env/migrations/E2E tests (see Must Do backlog), not landing UI
📋 Backlog (Next Tasks)
🔴 Must Do Before Launch
- Create
.env.localfrom.env.exampleand fill in real keys ← NOT DONE YET - Run Supabase migrations (
001→002→003) in SQL Editor ← NOT DONE YET - Test full auth flow (sign up → dashboard → create project → open studio)
- Test ffmpeg.wasm trimmer end-to-end in browser
🟡 UI Polish (Cursor screenshot-driven)
- Navbar: Video/Image Maker + Learn dropdowns (Renderforest-style, no mega menu)
- Landing polish pass:
rf-blue/rf-blue-lighttokens, Hero AI gradient, pricing Save 20% badge,#templatesscroll-mt-20 - Hero: animated video thumbnail preview cards (autoplay muted)
- TemplateCard: video preview on hover (autoplay muted loop, AnimatePresence fade)
- Studio: scene thumbnail auto-generated from Konva canvas (toDataURL)
- Studio: transition picker between scenes (fade, slide, zoom)
- Dashboard: skeleton loading states for project cards
- Mobile: studio pages desktop gate (under 768px shows
StudioMobileGate, not full editor)
🟢 Nice to Have
- Template system: pre-built
.aeptemplates for nexrender - Image editor: text curved/arc effect
- Image editor: sticker/icon library (200+ SVGs)
- Video studio: background color/gradient picker per scene
- Onboarding flow for new users (first project wizard)
- Usage limits per plan (enforced server-side)
🐛 Known Issues
| # | Issue | File | Priority |
|---|---|---|---|
| 1 | Brand name "CreatorStudio" in UI | ✅ Fixed 2026-05-21 — all 4 files updated | — |
| 2 | next.config.mjs was missing COOP/COEP headers |
✅ Fixed 2026-05-21 | — |
| 3 | Scene thumbnails are placeholder gray boxes | ✅ Fixed 2026-05-21 — thumbnailUrl via updateSceneThumbnail |
— |
| 4 | No loading/error state in trimmer if ffmpeg CDN is slow | TrimmerExportSection |
🟡 Medium |
| 5 | Image editor crop tool not fully implemented | ✅ Fixed 2026-05-21 — ImageCropControls, ImageCropOverlay, pixel crop in applyCrop |
— |
🏗️ Architecture Decisions
| Decision | Rationale |
|---|---|
| React-Konva for all canvas work | Consistent layer model across Video Studio and Image Editor |
| ffmpeg.wasm in Web Worker only | Never block main thread; SharedArrayBuffer requires COOP/COEP headers in next.config.mjs |
| Zustand for studio state (not React context) | Avoids re-render cascade on every canvas update |
| nexrender + aerender for final render | AE templates give highest quality output; RENDER_MOCK=true skips it in dev |
| Supabase RLS on all tables | Row-level security — users can only access their own data |
studio-canvas-stage.ts global stage ref |
Allows snapshot/export from any component without prop-drilling |
| Dynamic import for ImageEditorCanvas | Konva cannot run on server — ssr: false prevents SSR crash |
dev-mock-project.ts |
Dev POST mock; dev-project id skips load/save in studio editors |
🚀 Production-Ready Checklist
| Item | Status |
|---|---|
next.config.mjs — output: 'standalone' (Docker) |
✅ |
app/sitemap.ts — /, /video-maker, /image-maker, /templates, /pricing |
✅ |
app/robots.ts — allow public; disallow /dashboard, /studio, /api |
✅ |
Page metadata via createPageMetadata (public routes + studio layouts) |
✅ |
app/pricing/page.tsx — dedicated pricing route for SEO/sitemap |
✅ |
app/error.tsx — error boundary with reload |
✅ |
app/not-found.tsx — 404 with home CTA |
✅ |
npx tsc --noEmit clean |
✅ (re-run before deploy) |
.env.local + Supabase migrations |
⬜ Operator setup |
NEXT_PUBLIC_SITE_URL set to production domain |
⬜ Required for sitemap/OG URLs |
🔑 Environment Variables Checklist
| Variable | Status | Where to get it |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
⬜ Not set | Supabase → Settings → API |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
⬜ Not set | Supabase → Settings → API |
SUPABASE_SERVICE_ROLE_KEY |
⬜ Not set | Supabase → Settings → API |
STRIPE_SECRET_KEY |
⬜ Not set | Stripe Dashboard → API Keys |
STRIPE_WEBHOOK_SECRET |
⬜ Not set | Stripe → Webhooks → signing secret |
STRIPE_PRICE_PRO_MONTHLY |
⬜ Not set | Stripe → Products → price ID |
STRIPE_PRICE_PRO_ANNUAL |
⬜ Not set | Stripe → Products → price ID |
STRIPE_PRICE_BUSINESS_MONTHLY |
⬜ Not set | Stripe → Products → price ID |
STRIPE_PRICE_BUSINESS_ANNUAL |
⬜ Not set | Stripe → Products → price ID |
REMOVE_BG_API_KEY |
⬜ Not set | remove.bg → API |
RENDER_WORKER_URL |
✅ Default: http://localhost:3355 |
— |
RENDER_MOCK |
✅ Default: true |
Set to false when AE is configured |
NEXRENDER_TEMPLATE_SRC |
⬜ Not set | Path to your .aep template file |
NEXRENDER_BINARY |
⬜ Not set | Path to aerender executable |
📁 Key File Map (Quick Reference)
src/
├── app/
│ ├── page.tsx ← Landing page
│ ├── video-maker/page.tsx ← Video Maker product page
│ ├── image-maker/page.tsx ← Image Maker product page
│ ├── templates/page.tsx ← Templates gallery
│ ├── pricing/page.tsx ← Pricing (public SEO route)
│ ├── sitemap.ts / robots.ts ← SEO + crawler rules
│ ├── error.tsx / not-found.tsx ← Global error UI
│ ├── auth/page.tsx ← Sign in / Sign up
│ ├── dashboard/page.tsx ← User dashboard
│ ├── studio/
│ │ ├── video/new/page.tsx ← New video project onboarding
│ │ ├── video/[projectId]/page.tsx ← Video Creation Studio
│ │ ├── image/[projectId]/page.tsx ← Image Editor
│ │ └── trimmer/page.tsx ← Video Trimmer/Cropper
│ └── api/ ← All API routes
├── components/
│ ├── layout/ ← Navbar, Footer, SiteChrome
│ ├── sections/ ← Landing page sections
│ ├── studio/ ← Video Studio components
│ ├── image-editor/ ← Image Editor components
│ ├── trimmer/ ← Video Trimmer components
│ ├── dashboard/ ← Dashboard components
│ └── ui/ ← shadcn/ui (do not edit)
├── lib/
│ ├── studio-store.ts ← Video Studio Zustand store
│ ├── studio-types.ts ← Scene / Layer types
│ ├── image-editor-store.ts ← Image Editor Zustand store
│ └── supabase/ ← Supabase client helpers
├── hooks/ ← useCanvasKeyboard, useContainerSize, useIsMobile, etc.
└── workers/
└── ffmpeg-trim.worker.ts ← ffmpeg.wasm Web Worker
server/
├── render-worker.ts ← HTTP server (port 3355)
├── render-job-processor.ts ← nexrender job runner
└── nexrender-job-builder.ts ← builds AE job from scene JSON
supabase/
└── migrations/ ← SQL files, run in order in Supabase SQL Editor
📅 Session Log
| Date | What was done |
|---|---|
| 2026-05-21 | Full project scaffolded: all pages, studio modules, API routes, Supabase migrations, server render worker |
| 2026-05-21 | Fixed next.config.mjs — added COOP/COEP headers required for ffmpeg.wasm SharedArrayBuffer |
| 2026-05-21 | Created PROJECT_MEMORY.md — project brain for Cursor + Claude |
| 2026-05-21 | Fixed brand name in 4 files: metadata.ts, DashboardSidebar.tsx, ProductsMegaMenu.tsx, Navbar.tsx |
| 2026-05-21 | Discovered ProductsMegaMenu already fully built — moved from backlog to done |
| 2026-05-21 | Added PricingCompareTable with 5 feature sections matching Renderforest /subscription layout |
| 2026-05-21 | Scene thumbnails: thumbnailUrl on Scene, updateSceneThumbnail, DraggableSceneItem img preview |