From c61f58776723631d873f3d2b8dba55fb6c309480 Mon Sep 17 00:00:00 2001 From: "Soroush.Asadi" Date: Sun, 24 May 2026 17:37:21 +0330 Subject: [PATCH] feat: full studio build -- light theme, canvas thumbnails, i18n (fa/en) --- .cursor/debug-f6a22a.log | 8 + .cursorrules | 25 +- .env.example | 35 + PROJECT_MEMORY.md | 358 ++ components.json | 20 + messages/en.json | 245 + messages/fa.json | 245 + next.config.mjs | 51 +- package-lock.json | 5642 ++++++++++++++++- package.json | 48 +- server/nexrender-job-builder.ts | 173 + server/nexrender.d.ts | 13 + server/render-job-processor.ts | 247 + server/render-worker.ts | 71 + src/app/[locale]/auth/page.tsx | 28 + src/app/[locale]/dashboard/layout.tsx | 36 + src/app/[locale]/dashboard/page.tsx | 22 + src/app/[locale]/dashboard/settings/page.tsx | 26 + src/app/[locale]/error.tsx | 34 + src/app/[locale]/image-maker/page.tsx | 27 + src/app/[locale]/layout.tsx | 118 + src/app/[locale]/not-found.tsx | 27 + src/app/[locale]/page.tsx | 31 + src/app/[locale]/pricing/page.tsx | 19 + .../studio/image/[projectId]/page.tsx | 28 + src/app/[locale]/studio/image/layout.tsx | 18 + src/app/[locale]/studio/image/page.tsx | 5 + src/app/[locale]/studio/trimmer/layout.tsx | 18 + src/app/[locale]/studio/trimmer/page.tsx | 212 + .../studio/video/[projectId]/layout.tsx | 18 + .../studio/video/[projectId]/page.tsx | 28 + src/app/[locale]/studio/video/new/layout.tsx | 22 + src/app/[locale]/studio/video/new/page.tsx | 21 + src/app/[locale]/templates/[id]/page.tsx | 29 + src/app/[locale]/templates/page.tsx | 19 + src/app/[locale]/video-maker/page.tsx | 27 + src/app/api/checkout/route.ts | 90 + src/app/api/projects/[projectId]/route.ts | 159 + src/app/api/projects/route.ts | 126 + src/app/api/remove-bg/route.ts | 91 + src/app/api/render/[jobId]/status/route.ts | 30 + src/app/api/render/route.ts | 32 + src/app/api/webhooks/stripe/route.ts | 123 + src/app/auth/callback/route.ts | 21 + src/app/auth/sign-out/route.ts | 11 + src/app/globals.css | 83 +- src/app/layout.tsx | 39 +- src/app/opengraph-image.tsx | 59 + src/app/page.tsx | 101 - src/app/robots.ts | 15 + src/app/sitemap.ts | 21 + src/components/auth/AuthLoadingSpinner.tsx | 27 + src/components/auth/AuthPageContent.tsx | 323 + src/components/auth/SupabaseSetupNotice.tsx | 47 + src/components/auth/auth-schemas.ts | 14 + .../dashboard/DashboardEmptyState.tsx | 27 + .../dashboard/DashboardPlanBadge.tsx | 47 + .../dashboard/DashboardProjectsContent.tsx | 27 + .../dashboard/DashboardProjectsSection.tsx | 83 + src/components/dashboard/DashboardShell.tsx | 26 + src/components/dashboard/DashboardSidebar.tsx | 89 + .../dashboard/DashboardSidebarNav.tsx | 51 + src/components/dashboard/DashboardTopBar.tsx | 35 + src/components/dashboard/NewProjectMenu.tsx | 96 + src/components/dashboard/ProjectCard.tsx | 226 + .../dashboard/SkeletonProjectCard.tsx | 21 + .../image-editor/AiRemoveBgModal.tsx | 96 + .../image-editor/ImageCropControls.tsx | 79 + .../image-editor/ImageEditorLayout.tsx | 62 + .../image-editor/ImageEditorRightPanel.tsx | 46 + .../image-editor/ImageEditorToolbar.tsx | 112 + .../image-editor/ImageEditorTopBar.tsx | 160 + .../image-editor/canvas/ImageBaseLayer.tsx | 74 + .../image-editor/canvas/ImageCropOverlay.tsx | 53 + .../image-editor/canvas/ImageEditorCanvas.tsx | 246 + .../canvas/ImageEditorLayerNode.tsx | 180 + .../image-editor/canvas/VignetteOverlay.tsx | 39 + .../image-editor/panels/AdjustPanel.tsx | 50 + .../image-editor/panels/FiltersPanel.tsx | 50 + .../image-editor/panels/LayersPanel.tsx | 157 + .../image-maker/ImageMakerBeforeAfter.tsx | 43 + src/components/image-maker/ImageMakerCta.tsx | 32 + .../image-maker/ImageMakerFeatures.tsx | 91 + .../image-maker/ImageMakerGallery.tsx | 41 + src/components/image-maker/ImageMakerHero.tsx | 58 + .../image-maker/ImageMakerUseCases.tsx | 84 + .../image-maker/image-maker-gallery-data.ts | 20 + src/components/layout/Footer.tsx | 130 + src/components/layout/LanguageSwitcher.tsx | 56 + src/components/layout/Navbar.tsx | 175 + src/components/layout/NavbarMenuDropdown.tsx | 91 + src/components/layout/NavbarMobileMenu.tsx | 90 + src/components/layout/SiteChrome.tsx | 28 + src/components/sections/FAQ.tsx | 64 + src/components/sections/Hero.tsx | 105 + .../sections/HeroBackgroundBlobs.tsx | 54 + src/components/sections/HeroPreviewCards.tsx | 96 + src/components/sections/HowItWorks.tsx | 62 + .../sections/HowItWorksConnector.tsx | 23 + src/components/sections/HowItWorksStep.tsx | 72 + src/components/sections/Pricing.tsx | 48 + .../sections/PricingAnimatedPrice.tsx | 60 + src/components/sections/PricingBackground.tsx | 53 + .../sections/PricingBillingToggle.tsx | 57 + src/components/sections/PricingCard.tsx | 93 + .../sections/PricingCheckoutButton.tsx | 90 + .../sections/PricingCompareTable.tsx | 186 + .../sections/PricingCompareValue.tsx | 52 + .../sections/PricingCreditsBanner.tsx | 12 + .../sections/PricingFeatureList.tsx | 48 + src/components/sections/PricingFreeBanner.tsx | 27 + .../sections/ProductShowcaseCard.tsx | 100 + src/components/sections/ProductsShowcase.tsx | 96 + src/components/sections/SectionReveal.tsx | 41 + src/components/sections/TemplateCard.tsx | 143 + src/components/sections/TemplateGallery.tsx | 163 + src/components/sections/TestimonialCard.tsx | 57 + src/components/sections/Testimonials.tsx | 45 + src/components/sections/VideoPlayOverlay.tsx | 36 + src/components/sections/faq-data.ts | 61 + src/components/sections/how-it-works-data.ts | 40 + src/components/sections/pricing-data.ts | 259 + .../sections/template-gallery-data.ts | 137 + src/components/sections/testimonials-data.ts | 65 + src/components/studio/AddSceneMenu.tsx | 66 + src/components/studio/CanvasEditor.tsx | 228 + src/components/studio/DraggableSceneItem.tsx | 150 + .../studio/ProjectSaveIndicator.tsx | 79 + src/components/studio/PropertiesPanel.tsx | 69 + src/components/studio/RenderModal.tsx | 302 + src/components/studio/SceneBrowserCard.tsx | 161 + src/components/studio/SceneBrowserModal.tsx | 225 + src/components/studio/SceneItemActions.tsx | 49 + .../studio/SceneTransitionPicker.tsx | 73 + src/components/studio/StudioMobileGate.tsx | 45 + src/components/studio/StudioToolbar.tsx | 220 + src/components/studio/Timeline.tsx | 34 + src/components/studio/ToolbarIconButton.tsx | 40 + .../studio/canvas/CanvasLayerNode.tsx | 32 + .../studio/canvas/ImageLayerNode.tsx | 128 + .../studio/canvas/ShapeLayerNode.tsx | 110 + .../studio/canvas/TextLayerNode.tsx | 54 + .../studio/canvas/VideoLayerNode.tsx | 73 + .../studio/properties/CommonLayerControls.tsx | 125 + .../properties/ImageLayerProperties.tsx | 91 + .../studio/properties/PropertyControls.tsx | 214 + .../properties/ShapeLayerProperties.tsx | 58 + .../studio/properties/TextLayerProperties.tsx | 141 + .../studio/properties/useLayerUpdater.ts | 29 + .../studio/sidebar/AudioSidebarContent.tsx | 49 + .../studio/sidebar/AudioSidebarMusicTab.tsx | 168 + .../sidebar/AudioSidebarVoiceoverPane.tsx | 16 + .../studio/sidebar/ColorsCustomTab.tsx | 65 + .../studio/sidebar/ColorsPalettesTab.tsx | 71 + .../studio/sidebar/ColorsSidebarContent.tsx | 67 + .../sidebar/ColorsTemplatePreviewCard.tsx | 73 + .../studio/sidebar/FontSidebarContent.tsx | 43 + .../sidebar/SceneEditSidebarContent.tsx | 203 + .../studio/sidebar/SidebarPanelShell.tsx | 38 + .../studio/sidebar/TransitionPreviewTile.tsx | 51 + .../sidebar/TransitionsSidebarContent.tsx | 114 + .../studio/sidebar/TtsSidebarContent.tsx | 19 + .../sidebar/WatermarkSidebarContent.tsx | 100 + src/components/studio/timeline/AudioTrack.tsx | 58 + src/components/studio/timeline/SceneBlock.tsx | 109 + .../studio/timeline/SceneThumbnailBlock.tsx | 231 + .../studio/timeline/SceneThumbnailStrip.tsx | 119 + src/components/studio/timeline/SceneTrack.tsx | 41 + src/components/studio/timeline/TimeRuler.tsx | 93 + .../studio/timeline/TimelineActionRow.tsx | 49 + .../studio/timeline/TimelineControlBar.tsx | 148 + .../studio/timeline/TimelinePlayhead.tsx | 21 + .../studio/timeline/TimelineQuickActions.tsx | 43 + src/components/studio/video/CanvasArea.tsx | 48 + .../studio/video/ResizableStudioPanel.tsx | 74 + .../studio/video/StudioSidebarContent.tsx | 28 + .../studio/video/StudioSidebarDock.tsx | 145 + src/components/studio/video/StudioTopBar.tsx | 226 + .../studio/video/StudioTopBarSaveBadge.tsx | 67 + .../studio/video/StudioTopBarTextControls.tsx | 84 + .../studio/video/VideoNewOptionCard.tsx | 53 + .../studio/video/VideoNewPresetCard.tsx | 102 + .../studio/video/VideoProjectNewContent.tsx | 162 + .../studio/video/VideoStudioLayout.tsx | 66 + .../templates/TemplateDetailBreadcrumb.tsx | 42 + .../templates/TemplateDetailContent.tsx | 26 + .../templates/TemplateDetailExamples.tsx | 32 + .../templates/TemplateDetailInfo.tsx | 149 + .../templates/TemplateDetailPreview.tsx | 81 + .../templates/TemplateDetailRating.tsx | 56 + .../templates/TemplatesActiveFilters.tsx | 94 + .../templates/TemplatesPageContent.tsx | 26 + src/components/templates/TemplatesSidebar.tsx | 110 + .../video/VideoTemplateCompactCard.tsx | 141 + .../video/VideoTemplatesCarouselRow.tsx | 100 + .../video/VideoTemplatesCategorySidebar.tsx | 137 + .../video/VideoTemplatesFilterControls.tsx | 75 + .../templates/video/VideoTemplatesHero.tsx | 24 + .../video/VideoTemplatesPageContent.tsx | 154 + .../templates/video/VideoTemplatesToolbar.tsx | 97 + .../trimmer/TrimmerExportSection.tsx | 109 + src/components/trimmer/TrimmerStrip.tsx | 132 + src/components/trimmer/TrimmerUploadZone.tsx | 89 + .../trimmer/TrimmerVideoPreview.tsx | 142 + src/components/ui/accordion.tsx | 57 + src/components/ui/button.tsx | 58 + src/components/ui/dialog.tsx | 92 + src/components/ui/dropdown-menu.tsx | 202 + src/components/ui/optimized-image.tsx | 28 + src/components/ui/popover.tsx | 31 + src/components/ui/select.tsx | 159 + src/components/ui/sheet.tsx | 106 + src/components/ui/slider.tsx | 28 + src/components/ui/switch.tsx | 29 + src/components/ui/tabs.tsx | 52 + src/components/ui/toast.tsx | 71 + src/components/ui/toaster.tsx | 26 + src/components/ui/use-toast.ts | 129 + src/components/video-maker/VideoMakerCta.tsx | 32 + .../video-maker/VideoMakerEditorPreview.tsx | 93 + .../video-maker/VideoMakerFeatures.tsx | 91 + src/components/video-maker/VideoMakerHero.tsx | 54 + .../VideoMakerTemplateCarousel.tsx | 63 + .../video-maker/VideoMakerUseCases.tsx | 79 + src/hooks/useCanvasKeyboard.ts | 74 + src/hooks/useCanvasPreviewPlayback.ts | 93 + src/hooks/useContainerSize.ts | 30 + src/hooks/useDebouncedValue.ts | 14 + src/hooks/useImageProjectPersistence.ts | 140 + src/hooks/useIsMobile.ts | 34 + src/hooks/useStudioProjectPersistence.ts | 242 + src/hooks/useVideoThumbnails.ts | 82 + src/i18n/request.ts | 16 + src/i18n/routing.ts | 12 + src/lib/audio-music-genres.ts | 53 + src/lib/canvas-transform.ts | 21 + src/lib/create-project-from-template.ts | 65 + src/lib/create-video-project.ts | 51 + src/lib/dev-mock-project.ts | 20 + src/lib/dev-project-storage.ts | 49 + src/lib/ffmpeg-worker-client.ts | 128 + src/lib/image-editor-crop.ts | 114 + src/lib/image-editor-export.ts | 27 + src/lib/image-editor-filters.ts | 110 + src/lib/image-editor-konva.ts | 42 + src/lib/image-editor-stage-ref.ts | 11 + src/lib/image-editor-store.ts | 372 ++ src/lib/image-editor-transform.ts | 21 + src/lib/image-editor-types.ts | 62 + src/lib/image-placeholder.ts | 3 + src/lib/image-scene-data.ts | 109 + src/lib/metadata.ts | 72 + src/lib/navbar-menu-data.ts | 36 + src/lib/plans.ts | 49 + src/lib/profiles.ts | 54 + src/lib/project-api.ts | 74 + src/lib/project-defaults.ts | 48 + src/lib/project-ids.ts | 5 + src/lib/project-save-status.ts | 10 + src/lib/projects.ts | 86 + src/lib/render-jobs.ts | 76 + src/lib/render-presets.ts | 24 + src/lib/render-schemas.ts | 56 + src/lib/scene-browser-data.ts | 623 ++ src/lib/scene-transition-options.ts | 20 + src/lib/scene-transitions.ts | 47 + src/lib/stripe.ts | 20 + src/lib/studio-canvas-stage.ts | 11 + src/lib/studio-color-palettes.ts | 96 + src/lib/studio-history.ts | 31 + src/lib/studio-layer-props.ts | 120 + src/lib/studio-scene-data.ts | 151 + src/lib/studio-scene-thumbnail.ts | 21 + src/lib/studio-snapshot.ts | 13 + src/lib/studio-store.ts | 794 +++ src/lib/studio-timeline.ts | 112 + src/lib/studio-types.ts | 51 + src/lib/supabase.ts | 15 + src/lib/supabase/admin.ts | 19 + src/lib/supabase/config.ts | 6 + src/lib/supabase/middleware.ts | 38 + src/lib/supabase/server.ts | 32 + src/lib/template-preview-media.ts | 71 + src/lib/templates-catalog.ts | 144 + src/lib/trimmer-types.ts | 15 + src/lib/trimmer-utils.ts | 66 + src/lib/utils.ts | 6 + src/lib/video-templates-catalog.ts | 432 ++ src/middleware.ts | 36 + src/types/use-image.d.ts | 8 + src/workers/ffmpeg-trim.worker.ts | 165 + supabase/migrations/001_profiles.sql | 45 + supabase/migrations/002_render_jobs.sql | 49 + supabase/migrations/003_projects.sql | 56 + tailwind.config.ts | 106 +- 295 files changed, 29797 insertions(+), 265 deletions(-) create mode 100644 .cursor/debug-f6a22a.log create mode 100644 .env.example create mode 100644 PROJECT_MEMORY.md create mode 100644 components.json create mode 100644 messages/en.json create mode 100644 messages/fa.json create mode 100644 server/nexrender-job-builder.ts create mode 100644 server/nexrender.d.ts create mode 100644 server/render-job-processor.ts create mode 100644 server/render-worker.ts create mode 100644 src/app/[locale]/auth/page.tsx create mode 100644 src/app/[locale]/dashboard/layout.tsx create mode 100644 src/app/[locale]/dashboard/page.tsx create mode 100644 src/app/[locale]/dashboard/settings/page.tsx create mode 100644 src/app/[locale]/error.tsx create mode 100644 src/app/[locale]/image-maker/page.tsx create mode 100644 src/app/[locale]/layout.tsx create mode 100644 src/app/[locale]/not-found.tsx create mode 100644 src/app/[locale]/page.tsx create mode 100644 src/app/[locale]/pricing/page.tsx create mode 100644 src/app/[locale]/studio/image/[projectId]/page.tsx create mode 100644 src/app/[locale]/studio/image/layout.tsx create mode 100644 src/app/[locale]/studio/image/page.tsx create mode 100644 src/app/[locale]/studio/trimmer/layout.tsx create mode 100644 src/app/[locale]/studio/trimmer/page.tsx create mode 100644 src/app/[locale]/studio/video/[projectId]/layout.tsx create mode 100644 src/app/[locale]/studio/video/[projectId]/page.tsx create mode 100644 src/app/[locale]/studio/video/new/layout.tsx create mode 100644 src/app/[locale]/studio/video/new/page.tsx create mode 100644 src/app/[locale]/templates/[id]/page.tsx create mode 100644 src/app/[locale]/templates/page.tsx create mode 100644 src/app/[locale]/video-maker/page.tsx create mode 100644 src/app/api/checkout/route.ts create mode 100644 src/app/api/projects/[projectId]/route.ts create mode 100644 src/app/api/projects/route.ts create mode 100644 src/app/api/remove-bg/route.ts create mode 100644 src/app/api/render/[jobId]/status/route.ts create mode 100644 src/app/api/render/route.ts create mode 100644 src/app/api/webhooks/stripe/route.ts create mode 100644 src/app/auth/callback/route.ts create mode 100644 src/app/auth/sign-out/route.ts create mode 100644 src/app/opengraph-image.tsx delete mode 100644 src/app/page.tsx create mode 100644 src/app/robots.ts create mode 100644 src/app/sitemap.ts create mode 100644 src/components/auth/AuthLoadingSpinner.tsx create mode 100644 src/components/auth/AuthPageContent.tsx create mode 100644 src/components/auth/SupabaseSetupNotice.tsx create mode 100644 src/components/auth/auth-schemas.ts create mode 100644 src/components/dashboard/DashboardEmptyState.tsx create mode 100644 src/components/dashboard/DashboardPlanBadge.tsx create mode 100644 src/components/dashboard/DashboardProjectsContent.tsx create mode 100644 src/components/dashboard/DashboardProjectsSection.tsx create mode 100644 src/components/dashboard/DashboardShell.tsx create mode 100644 src/components/dashboard/DashboardSidebar.tsx create mode 100644 src/components/dashboard/DashboardSidebarNav.tsx create mode 100644 src/components/dashboard/DashboardTopBar.tsx create mode 100644 src/components/dashboard/NewProjectMenu.tsx create mode 100644 src/components/dashboard/ProjectCard.tsx create mode 100644 src/components/dashboard/SkeletonProjectCard.tsx create mode 100644 src/components/image-editor/AiRemoveBgModal.tsx create mode 100644 src/components/image-editor/ImageCropControls.tsx create mode 100644 src/components/image-editor/ImageEditorLayout.tsx create mode 100644 src/components/image-editor/ImageEditorRightPanel.tsx create mode 100644 src/components/image-editor/ImageEditorToolbar.tsx create mode 100644 src/components/image-editor/ImageEditorTopBar.tsx create mode 100644 src/components/image-editor/canvas/ImageBaseLayer.tsx create mode 100644 src/components/image-editor/canvas/ImageCropOverlay.tsx create mode 100644 src/components/image-editor/canvas/ImageEditorCanvas.tsx create mode 100644 src/components/image-editor/canvas/ImageEditorLayerNode.tsx create mode 100644 src/components/image-editor/canvas/VignetteOverlay.tsx create mode 100644 src/components/image-editor/panels/AdjustPanel.tsx create mode 100644 src/components/image-editor/panels/FiltersPanel.tsx create mode 100644 src/components/image-editor/panels/LayersPanel.tsx create mode 100644 src/components/image-maker/ImageMakerBeforeAfter.tsx create mode 100644 src/components/image-maker/ImageMakerCta.tsx create mode 100644 src/components/image-maker/ImageMakerFeatures.tsx create mode 100644 src/components/image-maker/ImageMakerGallery.tsx create mode 100644 src/components/image-maker/ImageMakerHero.tsx create mode 100644 src/components/image-maker/ImageMakerUseCases.tsx create mode 100644 src/components/image-maker/image-maker-gallery-data.ts create mode 100644 src/components/layout/Footer.tsx create mode 100644 src/components/layout/LanguageSwitcher.tsx create mode 100644 src/components/layout/Navbar.tsx create mode 100644 src/components/layout/NavbarMenuDropdown.tsx create mode 100644 src/components/layout/NavbarMobileMenu.tsx create mode 100644 src/components/layout/SiteChrome.tsx create mode 100644 src/components/sections/FAQ.tsx create mode 100644 src/components/sections/Hero.tsx create mode 100644 src/components/sections/HeroBackgroundBlobs.tsx create mode 100644 src/components/sections/HeroPreviewCards.tsx create mode 100644 src/components/sections/HowItWorks.tsx create mode 100644 src/components/sections/HowItWorksConnector.tsx create mode 100644 src/components/sections/HowItWorksStep.tsx create mode 100644 src/components/sections/Pricing.tsx create mode 100644 src/components/sections/PricingAnimatedPrice.tsx create mode 100644 src/components/sections/PricingBackground.tsx create mode 100644 src/components/sections/PricingBillingToggle.tsx create mode 100644 src/components/sections/PricingCard.tsx create mode 100644 src/components/sections/PricingCheckoutButton.tsx create mode 100644 src/components/sections/PricingCompareTable.tsx create mode 100644 src/components/sections/PricingCompareValue.tsx create mode 100644 src/components/sections/PricingCreditsBanner.tsx create mode 100644 src/components/sections/PricingFeatureList.tsx create mode 100644 src/components/sections/PricingFreeBanner.tsx create mode 100644 src/components/sections/ProductShowcaseCard.tsx create mode 100644 src/components/sections/ProductsShowcase.tsx create mode 100644 src/components/sections/SectionReveal.tsx create mode 100644 src/components/sections/TemplateCard.tsx create mode 100644 src/components/sections/TemplateGallery.tsx create mode 100644 src/components/sections/TestimonialCard.tsx create mode 100644 src/components/sections/Testimonials.tsx create mode 100644 src/components/sections/VideoPlayOverlay.tsx create mode 100644 src/components/sections/faq-data.ts create mode 100644 src/components/sections/how-it-works-data.ts create mode 100644 src/components/sections/pricing-data.ts create mode 100644 src/components/sections/template-gallery-data.ts create mode 100644 src/components/sections/testimonials-data.ts create mode 100644 src/components/studio/AddSceneMenu.tsx create mode 100644 src/components/studio/CanvasEditor.tsx create mode 100644 src/components/studio/DraggableSceneItem.tsx create mode 100644 src/components/studio/ProjectSaveIndicator.tsx create mode 100644 src/components/studio/PropertiesPanel.tsx create mode 100644 src/components/studio/RenderModal.tsx create mode 100644 src/components/studio/SceneBrowserCard.tsx create mode 100644 src/components/studio/SceneBrowserModal.tsx create mode 100644 src/components/studio/SceneItemActions.tsx create mode 100644 src/components/studio/SceneTransitionPicker.tsx create mode 100644 src/components/studio/StudioMobileGate.tsx create mode 100644 src/components/studio/StudioToolbar.tsx create mode 100644 src/components/studio/Timeline.tsx create mode 100644 src/components/studio/ToolbarIconButton.tsx create mode 100644 src/components/studio/canvas/CanvasLayerNode.tsx create mode 100644 src/components/studio/canvas/ImageLayerNode.tsx create mode 100644 src/components/studio/canvas/ShapeLayerNode.tsx create mode 100644 src/components/studio/canvas/TextLayerNode.tsx create mode 100644 src/components/studio/canvas/VideoLayerNode.tsx create mode 100644 src/components/studio/properties/CommonLayerControls.tsx create mode 100644 src/components/studio/properties/ImageLayerProperties.tsx create mode 100644 src/components/studio/properties/PropertyControls.tsx create mode 100644 src/components/studio/properties/ShapeLayerProperties.tsx create mode 100644 src/components/studio/properties/TextLayerProperties.tsx create mode 100644 src/components/studio/properties/useLayerUpdater.ts create mode 100644 src/components/studio/sidebar/AudioSidebarContent.tsx create mode 100644 src/components/studio/sidebar/AudioSidebarMusicTab.tsx create mode 100644 src/components/studio/sidebar/AudioSidebarVoiceoverPane.tsx create mode 100644 src/components/studio/sidebar/ColorsCustomTab.tsx create mode 100644 src/components/studio/sidebar/ColorsPalettesTab.tsx create mode 100644 src/components/studio/sidebar/ColorsSidebarContent.tsx create mode 100644 src/components/studio/sidebar/ColorsTemplatePreviewCard.tsx create mode 100644 src/components/studio/sidebar/FontSidebarContent.tsx create mode 100644 src/components/studio/sidebar/SceneEditSidebarContent.tsx create mode 100644 src/components/studio/sidebar/SidebarPanelShell.tsx create mode 100644 src/components/studio/sidebar/TransitionPreviewTile.tsx create mode 100644 src/components/studio/sidebar/TransitionsSidebarContent.tsx create mode 100644 src/components/studio/sidebar/TtsSidebarContent.tsx create mode 100644 src/components/studio/sidebar/WatermarkSidebarContent.tsx create mode 100644 src/components/studio/timeline/AudioTrack.tsx create mode 100644 src/components/studio/timeline/SceneBlock.tsx create mode 100644 src/components/studio/timeline/SceneThumbnailBlock.tsx create mode 100644 src/components/studio/timeline/SceneThumbnailStrip.tsx create mode 100644 src/components/studio/timeline/SceneTrack.tsx create mode 100644 src/components/studio/timeline/TimeRuler.tsx create mode 100644 src/components/studio/timeline/TimelineActionRow.tsx create mode 100644 src/components/studio/timeline/TimelineControlBar.tsx create mode 100644 src/components/studio/timeline/TimelinePlayhead.tsx create mode 100644 src/components/studio/timeline/TimelineQuickActions.tsx create mode 100644 src/components/studio/video/CanvasArea.tsx create mode 100644 src/components/studio/video/ResizableStudioPanel.tsx create mode 100644 src/components/studio/video/StudioSidebarContent.tsx create mode 100644 src/components/studio/video/StudioSidebarDock.tsx create mode 100644 src/components/studio/video/StudioTopBar.tsx create mode 100644 src/components/studio/video/StudioTopBarSaveBadge.tsx create mode 100644 src/components/studio/video/StudioTopBarTextControls.tsx create mode 100644 src/components/studio/video/VideoNewOptionCard.tsx create mode 100644 src/components/studio/video/VideoNewPresetCard.tsx create mode 100644 src/components/studio/video/VideoProjectNewContent.tsx create mode 100644 src/components/studio/video/VideoStudioLayout.tsx create mode 100644 src/components/templates/TemplateDetailBreadcrumb.tsx create mode 100644 src/components/templates/TemplateDetailContent.tsx create mode 100644 src/components/templates/TemplateDetailExamples.tsx create mode 100644 src/components/templates/TemplateDetailInfo.tsx create mode 100644 src/components/templates/TemplateDetailPreview.tsx create mode 100644 src/components/templates/TemplateDetailRating.tsx create mode 100644 src/components/templates/TemplatesActiveFilters.tsx create mode 100644 src/components/templates/TemplatesPageContent.tsx create mode 100644 src/components/templates/TemplatesSidebar.tsx create mode 100644 src/components/templates/video/VideoTemplateCompactCard.tsx create mode 100644 src/components/templates/video/VideoTemplatesCarouselRow.tsx create mode 100644 src/components/templates/video/VideoTemplatesCategorySidebar.tsx create mode 100644 src/components/templates/video/VideoTemplatesFilterControls.tsx create mode 100644 src/components/templates/video/VideoTemplatesHero.tsx create mode 100644 src/components/templates/video/VideoTemplatesPageContent.tsx create mode 100644 src/components/templates/video/VideoTemplatesToolbar.tsx create mode 100644 src/components/trimmer/TrimmerExportSection.tsx create mode 100644 src/components/trimmer/TrimmerStrip.tsx create mode 100644 src/components/trimmer/TrimmerUploadZone.tsx create mode 100644 src/components/trimmer/TrimmerVideoPreview.tsx create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/optimized-image.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/toast.tsx create mode 100644 src/components/ui/toaster.tsx create mode 100644 src/components/ui/use-toast.ts create mode 100644 src/components/video-maker/VideoMakerCta.tsx create mode 100644 src/components/video-maker/VideoMakerEditorPreview.tsx create mode 100644 src/components/video-maker/VideoMakerFeatures.tsx create mode 100644 src/components/video-maker/VideoMakerHero.tsx create mode 100644 src/components/video-maker/VideoMakerTemplateCarousel.tsx create mode 100644 src/components/video-maker/VideoMakerUseCases.tsx create mode 100644 src/hooks/useCanvasKeyboard.ts create mode 100644 src/hooks/useCanvasPreviewPlayback.ts create mode 100644 src/hooks/useContainerSize.ts create mode 100644 src/hooks/useDebouncedValue.ts create mode 100644 src/hooks/useImageProjectPersistence.ts create mode 100644 src/hooks/useIsMobile.ts create mode 100644 src/hooks/useStudioProjectPersistence.ts create mode 100644 src/hooks/useVideoThumbnails.ts create mode 100644 src/i18n/request.ts create mode 100644 src/i18n/routing.ts create mode 100644 src/lib/audio-music-genres.ts create mode 100644 src/lib/canvas-transform.ts create mode 100644 src/lib/create-project-from-template.ts create mode 100644 src/lib/create-video-project.ts create mode 100644 src/lib/dev-mock-project.ts create mode 100644 src/lib/dev-project-storage.ts create mode 100644 src/lib/ffmpeg-worker-client.ts create mode 100644 src/lib/image-editor-crop.ts create mode 100644 src/lib/image-editor-export.ts create mode 100644 src/lib/image-editor-filters.ts create mode 100644 src/lib/image-editor-konva.ts create mode 100644 src/lib/image-editor-stage-ref.ts create mode 100644 src/lib/image-editor-store.ts create mode 100644 src/lib/image-editor-transform.ts create mode 100644 src/lib/image-editor-types.ts create mode 100644 src/lib/image-placeholder.ts create mode 100644 src/lib/image-scene-data.ts create mode 100644 src/lib/metadata.ts create mode 100644 src/lib/navbar-menu-data.ts create mode 100644 src/lib/plans.ts create mode 100644 src/lib/profiles.ts create mode 100644 src/lib/project-api.ts create mode 100644 src/lib/project-defaults.ts create mode 100644 src/lib/project-ids.ts create mode 100644 src/lib/project-save-status.ts create mode 100644 src/lib/projects.ts create mode 100644 src/lib/render-jobs.ts create mode 100644 src/lib/render-presets.ts create mode 100644 src/lib/render-schemas.ts create mode 100644 src/lib/scene-browser-data.ts create mode 100644 src/lib/scene-transition-options.ts create mode 100644 src/lib/scene-transitions.ts create mode 100644 src/lib/stripe.ts create mode 100644 src/lib/studio-canvas-stage.ts create mode 100644 src/lib/studio-color-palettes.ts create mode 100644 src/lib/studio-history.ts create mode 100644 src/lib/studio-layer-props.ts create mode 100644 src/lib/studio-scene-data.ts create mode 100644 src/lib/studio-scene-thumbnail.ts create mode 100644 src/lib/studio-snapshot.ts create mode 100644 src/lib/studio-store.ts create mode 100644 src/lib/studio-timeline.ts create mode 100644 src/lib/studio-types.ts create mode 100644 src/lib/supabase.ts create mode 100644 src/lib/supabase/admin.ts create mode 100644 src/lib/supabase/config.ts create mode 100644 src/lib/supabase/middleware.ts create mode 100644 src/lib/supabase/server.ts create mode 100644 src/lib/template-preview-media.ts create mode 100644 src/lib/templates-catalog.ts create mode 100644 src/lib/trimmer-types.ts create mode 100644 src/lib/trimmer-utils.ts create mode 100644 src/lib/utils.ts create mode 100644 src/lib/video-templates-catalog.ts create mode 100644 src/middleware.ts create mode 100644 src/types/use-image.d.ts create mode 100644 src/workers/ffmpeg-trim.worker.ts create mode 100644 supabase/migrations/001_profiles.sql create mode 100644 supabase/migrations/002_render_jobs.sql create mode 100644 supabase/migrations/003_projects.sql 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` — `