11/**
2- * BuilderProvider wraps CTA providers and handles initialization
2+ * BuilderProvider - Wraps CTA providers and handles initialization
33 */
44
55import * as React from 'react'
@@ -14,22 +14,54 @@ import {
1414 useBuilderSearch ,
1515 useInitializeAddonsFromUrl ,
1616} from './hooks/useBuilderSearch'
17+ import { useCapabilities } from '~/hooks/useCapabilities'
18+ import { hasCapability } from '~/db/types'
1719
1820type BuilderProviderProps = {
1921 children : React . ReactNode
2022}
2123
22- function BuilderProviderInner ( { children } : BuilderProviderProps ) {
23- // Initialize manager to start CTA engine
24- useManager ( )
24+ // Context for lazy preview activation
25+ type PreviewContextValue = {
26+ canPreview : boolean
27+ previewActivated : boolean
28+ activatePreview : ( ) => void
29+ }
2530
26- // Sync URL state with CTA state
31+ const PreviewContext = React . createContext < PreviewContextValue > ( {
32+ canPreview : false ,
33+ previewActivated : false ,
34+ activatePreview : ( ) => { } ,
35+ } )
36+
37+ export function usePreviewContext ( ) {
38+ return React . useContext ( PreviewContext )
39+ }
40+
41+ // Inner component that only runs after CTA is ready
42+ // This avoids calling useAddOns before data is loaded (which has a bug)
43+ function BuilderReadyInner ( { children } : BuilderProviderProps ) {
44+ // Sync URL state with CTA state (uses useAddOns internally)
2745 useBuilderSearch ( )
2846
2947 // Initialize addons from URL after registry loads
3048 useInitializeAddonsFromUrl ( )
3149
32- const ready = useReady ( )
50+ // Check if user can use preview (admin only for now)
51+ const capabilities = useCapabilities ( )
52+ const canPreview = hasCapability ( capabilities , 'admin' )
53+
54+ // Lazy preview activation - only start WebContainer when user clicks Preview
55+ const [ previewActivated , setPreviewActivated ] = React . useState ( false )
56+ const activatePreview = React . useCallback ( ( ) => {
57+ setPreviewActivated ( true )
58+ } , [ ] )
59+
60+ const previewContextValue = React . useMemo (
61+ ( ) => ( { canPreview, previewActivated, activatePreview } ) ,
62+ [ canPreview , previewActivated , activatePreview ] ,
63+ )
64+
3365 const dryRun = useDryRun ( )
3466
3567 // Convert dry run files to WebContainer format
@@ -42,17 +74,60 @@ function BuilderProviderInner({ children }: BuilderProviderProps) {
4274 } ) )
4375 } , [ dryRunFiles ] )
4476
45- if ( ! ready ) {
46- return < BuilderLoading />
77+ // Only load WebContainer if user can preview AND has activated it
78+ if ( ! canPreview || ! previewActivated ) {
79+ return (
80+ < PreviewContext . Provider value = { previewContextValue } >
81+ { children }
82+ </ PreviewContext . Provider >
83+ )
4784 }
4885
4986 return (
50- < WebContainerProvider projectFiles = { projectFiles } >
51- { children }
52- </ WebContainerProvider >
87+ < PreviewContext . Provider value = { previewContextValue } >
88+ < WebContainerProvider projectFiles = { projectFiles } >
89+ { children }
90+ </ WebContainerProvider >
91+ </ PreviewContext . Provider >
5392 )
5493}
5594
95+ function BuilderProviderInner ( { children } : BuilderProviderProps ) {
96+ // Initialize manager to start CTA engine
97+ useManager ( )
98+
99+ const ready = useReady ( )
100+
101+ // Delay showing loading state to avoid flash during HMR
102+ // HMR typically re-initializes within ~100ms, so we wait a bit before showing loading
103+ const [ showLoading , setShowLoading ] = React . useState ( false )
104+
105+ React . useEffect ( ( ) => {
106+ if ( ready ) {
107+ setShowLoading ( false )
108+ return
109+ }
110+
111+ // Only show loading after a short delay to avoid HMR flash
112+ const timeout = setTimeout ( ( ) => {
113+ setShowLoading ( true )
114+ } , 150 )
115+
116+ return ( ) => clearTimeout ( timeout )
117+ } , [ ready ] )
118+
119+ if ( ! ready && showLoading ) {
120+ return < BuilderLoading />
121+ }
122+
123+ if ( ! ready ) {
124+ // Brief moment before showing loading - render nothing or a minimal placeholder
125+ return null
126+ }
127+
128+ return < BuilderReadyInner > { children } </ BuilderReadyInner >
129+ }
130+
56131export function BuilderProvider ( { children } : BuilderProviderProps ) {
57132 return (
58133 < CTAProvider >
0 commit comments