@@ -12,7 +12,10 @@ import { SidebarTabs } from "@/components/layout/sidebar-tabs";
1212import { useBrandLogoAutoApply } from "@/hooks/use-brand-logo-auto-apply" ;
1313import { usePlaygroundController } from "@/hooks/use-playground-controller" ;
1414import { useUserTier } from "@/hooks/use-user-tier" ;
15- import { EXPORT_ORIENTATION_DIMENSIONS , ORIENTATION_DIMENSIONS } from "@/domain/layout/screenshot-mode" ;
15+ import {
16+ EXPORT_ORIENTATION_DIMENSIONS ,
17+ ORIENTATION_DIMENSIONS ,
18+ } from "@/domain/layout/screenshot-mode" ;
1619import {
1720 assetsAtom ,
1821 baseConfigAtom ,
@@ -42,59 +45,67 @@ import { SidebarErrorBoundary } from "@/components/errors/sidebar-error-boundary
4245import { MemoryErrorBoundary } from "@/components/errors/memory-error-boundary" ;
4346import { InAppUpdateBanner } from "@/components/layout/in-app-update-banner" ;
4447import { useOnboardingStatus } from "@/hooks/use-onboarding-status" ;
45- import { OnboardingModal , type BrandProfilePayload } from "@/components/onboarding/onboarding-modal" ;
48+ import {
49+ OnboardingModal ,
50+ type BrandProfilePayload ,
51+ } from "@/components/onboarding/onboarding-modal" ;
4652import { listPersonalBackgrounds } from "@/domain/backgrounds/background-service" ;
53+ import { ExportSuccessModal } from "@/components/post-export" ;
54+ import { showExportSheetAtom , exportThumbnailAtom } from "@/hooks/atoms" ;
55+ import { useRouter } from "next/navigation" ;
56+ import { track } from "@/lib/analytics" ;
4757
4858const FeedbackModal = dynamic (
49- ( ) => import ( "@/components/feedback/feedback-modal" ) . then ( mod => ( { default : mod . FeedbackModal } ) ) ,
50- { ssr : false }
59+ ( ) =>
60+ import ( "@/components/feedback/feedback-modal" ) . then ( ( mod ) => ( { default : mod . FeedbackModal } ) ) ,
61+ { ssr : false } ,
5162) ;
5263
53- function ExportContainer ( {
54- width,
55- height,
56- orientation
57- } : {
58- width : number ;
64+ function ExportContainer ( {
65+ width,
66+ height,
67+ orientation,
68+ } : {
69+ width : number ;
5970 height : number ;
6071 orientation : Orientation ;
6172} ) {
6273 // Use preview dimensions as base (matching what user sees)
6374 const baseDims = ORIENTATION_DIMENSIONS [ orientation ] ;
6475 const scale = width / baseDims . width ;
65-
76+
6677 return (
78+ < div
79+ id = "export-container"
80+ style = { {
81+ position : "fixed" ,
82+ top : 0 ,
83+ left : 0 ,
84+ width : `${ width } px` ,
85+ height : `${ height } px` ,
86+ zIndex : - 100 ,
87+ overflow : "hidden" ,
88+ visibility : "visible" ,
89+ background : "white" ,
90+ WebkitFontSmoothing : "antialiased" ,
91+ MozOsxFontSmoothing : "grayscale" ,
92+ textRendering : "optimizeLegibility" ,
93+ display : "flex" ,
94+ alignItems : "center" ,
95+ justifyContent : "center" ,
96+ } }
97+ >
6798 < div
68- id = "export-container"
6999 style = { {
70- position : "fixed" ,
71- top : 0 ,
72- left : 0 ,
73- width : `${ width } px` ,
74- height : `${ height } px` ,
75- zIndex : - 100 ,
76- overflow : "hidden" ,
77- visibility : "visible" ,
78- background : "white" ,
79- WebkitFontSmoothing : "antialiased" ,
80- MozOsxFontSmoothing : "grayscale" ,
81- textRendering : "optimizeLegibility" ,
82- display : "flex" ,
83- alignItems : "center" ,
84- justifyContent : "center" ,
100+ width : `${ baseDims . width } px` ,
101+ height : `${ baseDims . height } px` ,
102+ transformOrigin : "center" ,
103+ transform : `scale(${ scale } )` ,
85104 } }
86105 >
87- < div
88- style = { {
89- width : `${ baseDims . width } px` ,
90- height : `${ baseDims . height } px` ,
91- transformOrigin : "center" ,
92- transform : `scale(${ scale } )` ,
93- } }
94- >
95- < CoverPreview isStatic />
96- </ div >
106+ < CoverPreview isStatic />
97107 </ div >
108+ </ div >
98109 ) ;
99110}
100111
@@ -132,12 +143,18 @@ function PlaygroundPageInner({
132143 initialIsAuthenticated,
133144 initialOnboardingOpen,
134145} : PlaygroundPageProps ) {
146+ const router = useRouter ( ) ;
135147 const orientation = useAtomValue ( orientationAtom ) ;
136148 const config = useAtomValue ( configAtom ) ;
137149 const personalBackgrounds = useAtomValue ( personalBackgroundsAtom ) ;
138150 const [ feedbackModalOpen , setFeedbackModalOpen ] = useAtom ( feedbackModalOpenAtom ) ;
139151 const [ feedbackScreenshot , setFeedbackScreenshot ] = useState < string | null > ( null ) ;
140152
153+ // Export success sheet state
154+ const [ showExportSheet , setShowExportSheet ] = useAtom ( showExportSheetAtom ) ;
155+ const exportThumbnail = useAtomValue ( exportThumbnailAtom ) ;
156+ const setExportThumbnail = useSetAtom ( exportThumbnailAtom ) ;
157+
141158 const { isBrandUser } = useUserTier ( ) ;
142159
143160 // Auto-apply brand logo for NEW designs (not loaded from memory)
@@ -150,6 +167,31 @@ function PlaygroundPageInner({
150167 setFeedbackScreenshot ( screenshot ) ;
151168 setFeedbackModalOpen ( true ) ;
152169 } ;
170+
171+ // Handle export sheet actions
172+ const handleExportSheetClose = useCallback ( ( ) => {
173+ setShowExportSheet ( false ) ;
174+ setExportThumbnail ( null ) ;
175+ track ( "export_sheet_dismissed" , {
176+ dismiss_method : "button" ,
177+ } ) ;
178+ } , [ setShowExportSheet , setExportThumbnail ] ) ;
179+
180+ const handleExportSheetSignup = useCallback ( ( ) => {
181+ track ( "export_sheet_signup_clicked" ) ;
182+ setShowExportSheet ( false ) ;
183+ setExportThumbnail ( null ) ;
184+ router . push ( "/auth" ) ;
185+ } , [ router , setShowExportSheet , setExportThumbnail ] ) ;
186+
187+ const handleExportSheetFeedback = useCallback ( ( ) => {
188+ track ( "export_sheet_feedback_clicked" ) ;
189+ setShowExportSheet ( false ) ;
190+ setExportThumbnail ( null ) ;
191+ setFeedbackScreenshot ( null ) ; // Clear stale screenshot to prevent re-sending old canvas
192+ setFeedbackModalOpen ( true ) ;
193+ } , [ setShowExportSheet , setExportThumbnail , setFeedbackModalOpen ] ) ;
194+
153195 // Memory hook for loading items
154196 const {
155197 loadMemoryItem,
@@ -201,7 +243,12 @@ function PlaygroundPageInner({
201243 if ( ! shouldOpenOnboarding ) return ;
202244 if ( hasLoadedOnboardingProfile || isOnboardingProfileLoading ) return ;
203245 void loadOnboardingProfile ( ) ;
204- } , [ hasLoadedOnboardingProfile , isOnboardingProfileLoading , loadOnboardingProfile , shouldOpenOnboarding ] ) ;
246+ } , [
247+ hasLoadedOnboardingProfile ,
248+ isOnboardingProfileLoading ,
249+ loadOnboardingProfile ,
250+ shouldOpenOnboarding ,
251+ ] ) ;
205252
206253 useEffect ( ( ) => {
207254 if ( ! shouldOpenOnboarding || ! hasLoadedOnboardingProfile ) return ;
@@ -271,7 +318,9 @@ function PlaygroundPageInner({
271318 if ( typeof window !== "undefined" ) {
272319 const hasIdleCallback = "requestIdleCallback" in window ;
273320 if ( hasIdleCallback ) {
274- prefetchBackgroundsHandleRef . current = window . requestIdleCallback ( prefetch , { timeout : 3000 } ) ;
321+ prefetchBackgroundsHandleRef . current = window . requestIdleCallback ( prefetch , {
322+ timeout : 3000 ,
323+ } ) ;
275324 } else {
276325 // In browser context, setTimeout returns a number (not Node's Timeout object)
277326 prefetchBackgroundsHandleRef . current = Number ( window . setTimeout ( prefetch , 1200 ) ) ;
@@ -330,10 +379,7 @@ function PlaygroundPageInner({
330379 } = usePlaygroundController ( { demoEnabled : ! isLoggedIn } ) ;
331380
332381 // Only show loading when actually loading a specific memory item from URL
333- const showLoadingState =
334- isLoggedIn &&
335- Boolean ( initialMemoryItemId ) &&
336- ! loadedItemId ;
382+ const showLoadingState = isLoggedIn && Boolean ( initialMemoryItemId ) && ! loadedItemId ;
337383
338384 const showEmptyState = useMemo ( ( ) => {
339385 const hasText = Boolean ( config . text . title ?. trim ( ) || config . text . subtitle ?. trim ( ) ) ;
@@ -453,10 +499,7 @@ function PlaygroundPageInner({
453499 { /* Right: Sidebar - spans full height from below nav */ }
454500 < SidebarErrorBoundary >
455501 < div className = "hidden h-full min-h-0 w-80 overflow-hidden border-l border-border bg-background sm:flex sm:flex-col" >
456- < SidebarTabs
457- onUploadAsset = { handleFileProcess }
458- onFeedbackClick = { handleFeedbackClick }
459- />
502+ < SidebarTabs onUploadAsset = { handleFileProcess } onFeedbackClick = { handleFeedbackClick } />
460503 </ div >
461504 </ SidebarErrorBoundary >
462505 </ div >
@@ -502,6 +545,14 @@ function PlaygroundPageInner({
502545 onOpenChange = { setFeedbackModalOpen }
503546 screenshotDataUrl = { feedbackScreenshot }
504547 />
548+
549+ < ExportSuccessModal
550+ isOpen = { showExportSheet }
551+ onClose = { handleExportSheetClose }
552+ onSignup = { handleExportSheetSignup }
553+ onFeedback = { handleExportSheetFeedback }
554+ thumbnailUrl = { exportThumbnail ?? undefined }
555+ />
505556 </ main >
506557 ) ;
507558}
0 commit comments