@@ -1551,6 +1551,7 @@ interface CmsPanelProps {
15511551 onDuplicateVariant : ( variantIdx : number ) => void ;
15521552 onRemoveVariant : ( variantIdx : number ) => void ;
15531553 onRemoveAllVariants : ( ) => void ;
1554+ onRemoveAllPageVariants ?: ( ) => void ;
15541555 pageVariants ?: GetPageSectionsOutput [ "pageVariants" ] ;
15551556 selectedPageVariant ?: number | null ;
15561557 onSelectPageVariant ?: ( idx : number ) => void ;
@@ -1605,6 +1606,7 @@ function CmsPanel({
16051606 onDuplicateVariant,
16061607 onRemoveVariant,
16071608 onRemoveAllVariants,
1609+ onRemoveAllPageVariants,
16081610 pageVariants,
16091611 selectedPageVariant,
16101612 onSelectPageVariant,
@@ -1635,6 +1637,11 @@ function CmsPanel({
16351637 const [ variantCardOpen , setVariantCardOpen ] = useState < boolean > (
16361638 ( ) => localStorage . getItem ( "variant-card-open" ) !== "false" ,
16371639 ) ;
1640+
1641+ const [ discardSectionVariantsConfirm , setDiscardSectionVariantsConfirm ] =
1642+ useState ( false ) ;
1643+ const [ discardPageVariantsConfirm , setDiscardPageVariantsConfirm ] =
1644+ useState ( false ) ;
16381645 const toggleVariantCard = ( ) => {
16391646 setVariantCardOpen ( ( prev ) => {
16401647 const next = ! prev ;
@@ -1705,6 +1712,7 @@ function CmsPanel({
17051712 : undefined ;
17061713
17071714 return (
1715+ < >
17081716 < div
17091717 className = "flex h-full w-full flex-col overflow-hidden bg-background transition-colors"
17101718 style = {
@@ -1761,7 +1769,7 @@ function CmsPanel({
17611769 actions = {
17621770 < button
17631771 type = "button"
1764- onClick = { onRemoveAllVariants }
1772+ onClick = { ( ) => setDiscardSectionVariantsConfirm ( true ) }
17651773 className = "shrink-0 rounded p-1 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
17661774 title = "Remove all variants"
17671775 >
@@ -2059,6 +2067,19 @@ function CmsPanel({
20592067 ) : isPageVariantList ? (
20602068 /* ── Page-level variant list ─────────────────────────────── */
20612069 < div className = "flex min-h-0 flex-1 flex-col overflow-hidden" >
2070+ < div className = "shrink-0 flex items-center justify-between border-b px-3 py-2" >
2071+ < span className = "text-xs text-muted-foreground" >
2072+ Page variants
2073+ </ span >
2074+ < button
2075+ type = "button"
2076+ onClick = { ( ) => setDiscardPageVariantsConfirm ( true ) }
2077+ className = "shrink-0 rounded p-1 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
2078+ title = "Discard all page variants"
2079+ >
2080+ < Trash2 className = "h-3.5 w-3.5" />
2081+ </ button >
2082+ </ div >
20622083 < div className = "min-h-0 flex-1 overflow-y-auto p-2" >
20632084 < DndContext
20642085 sensors = { sensors }
@@ -2299,6 +2320,61 @@ function CmsPanel({
22992320 </ div >
23002321 ) }
23012322 </ div >
2323+
2324+ < AlertDialog
2325+ open = { discardSectionVariantsConfirm }
2326+ onOpenChange = { setDiscardSectionVariantsConfirm }
2327+ >
2328+ < AlertDialogContent >
2329+ < AlertDialogHeader >
2330+ < AlertDialogTitle > Remove all variants?</ AlertDialogTitle >
2331+ < AlertDialogDescription >
2332+ This will remove all variants from this section and keep only the
2333+ default content. This action cannot be undone.
2334+ </ AlertDialogDescription >
2335+ </ AlertDialogHeader >
2336+ < AlertDialogFooter >
2337+ < AlertDialogCancel > Cancel</ AlertDialogCancel >
2338+ < AlertDialogAction
2339+ className = "bg-destructive text-destructive-foreground hover:bg-destructive/90"
2340+ onClick = { ( ) => {
2341+ setDiscardSectionVariantsConfirm ( false ) ;
2342+ onRemoveAllVariants ( ) ;
2343+ } }
2344+ >
2345+ Remove all variants
2346+ </ AlertDialogAction >
2347+ </ AlertDialogFooter >
2348+ </ AlertDialogContent >
2349+ </ AlertDialog >
2350+
2351+ < AlertDialog
2352+ open = { discardPageVariantsConfirm }
2353+ onOpenChange = { setDiscardPageVariantsConfirm }
2354+ >
2355+ < AlertDialogContent >
2356+ < AlertDialogHeader >
2357+ < AlertDialogTitle > Discard all page variants?</ AlertDialogTitle >
2358+ < AlertDialogDescription >
2359+ This will revert the page to a single version using the default
2360+ variant. All other variants will be permanently removed.
2361+ </ AlertDialogDescription >
2362+ </ AlertDialogHeader >
2363+ < AlertDialogFooter >
2364+ < AlertDialogCancel > Cancel</ AlertDialogCancel >
2365+ < AlertDialogAction
2366+ className = "bg-destructive text-destructive-foreground hover:bg-destructive/90"
2367+ onClick = { ( ) => {
2368+ setDiscardPageVariantsConfirm ( false ) ;
2369+ onRemoveAllPageVariants ?.( ) ;
2370+ } }
2371+ >
2372+ Discard all variants
2373+ </ AlertDialogAction >
2374+ </ AlertDialogFooter >
2375+ </ AlertDialogContent >
2376+ </ AlertDialog >
2377+ </ >
23022378 ) ;
23032379}
23042380
@@ -4151,6 +4227,66 @@ function FileExplorerWorkspace({
41514227 } , 300 ) ;
41524228 } ;
41534229
4230+ const handleRemoveAllPageVariants = ( ) => {
4231+ const snap = cmsDataRef . current ;
4232+ if ( ! snap ?. pageVariants || ! app || ! userEnv ) return ;
4233+
4234+ const mv = snap . pageData . sections as Record < string , unknown > ;
4235+ const rawVariants = (
4236+ mv . variants as Array < { value : unknown ; rule : Record < string , unknown > } >
4237+ ) ?? [ ] ;
4238+
4239+ const ALWAYS_TYPES = [
4240+ "website/matchers/always.ts" ,
4241+ "$live/matchers/MatchAlways.ts" ,
4242+ ] ;
4243+ const alwaysVariant = rawVariants . find ( ( v ) => {
4244+ const rt = ( v . rule . __resolveType as string ) ?? "" ;
4245+ return ALWAYS_TYPES . includes ( rt ) || rt === "" ;
4246+ } ) ;
4247+ const baseValue = alwaysVariant ?. value ?? rawVariants [ 0 ] ?. value ?? [ ] ;
4248+
4249+ const updatedPageData = { ...snap . pageData , sections : baseValue } ;
4250+
4251+ const alwaysDisplayVariant =
4252+ snap . pageVariants . find ( ( pv ) => {
4253+ const rt = ( pv . rule . __resolveType as string ) ?? "" ;
4254+ return ALWAYS_TYPES . includes ( rt ) || rt === "" ;
4255+ } ) ?? snap . pageVariants [ 0 ] ;
4256+
4257+ const next : GetPageSectionsOutput = {
4258+ ...snap ,
4259+ pageData : updatedPageData ,
4260+ pageVariants : undefined ,
4261+ sections : alwaysDisplayVariant ?. sections ?? snap . sections ,
4262+ } ;
4263+ setCmsData ( next ) ;
4264+ cmsDataRef . current = next ;
4265+ setCmsSelectedPageVariant ( null ) ;
4266+ cmsSelectedPageVariantRef . current = null ;
4267+
4268+ setCmsAutoSaving ( true ) ;
4269+ if ( cmsAutoSaveTimerRef . current ) clearTimeout ( cmsAutoSaveTimerRef . current ) ;
4270+ cmsAutoSaveTimerRef . current = setTimeout ( async ( ) => {
4271+ try {
4272+ const result = await app . callServerTool ( {
4273+ name : "write_file" ,
4274+ arguments : {
4275+ env : userEnv ,
4276+ filepath : next . filePath ,
4277+ content : JSON . stringify ( updatedPageData , null , 2 ) ,
4278+ } ,
4279+ } ) ;
4280+ if ( result ?. isError ) throw new Error ( "write_file failed" ) ;
4281+ setPreviewRefreshKey ( ( k ) => k + 1 ) ;
4282+ } catch {
4283+ toast . error ( "Auto-save failed" ) ;
4284+ } finally {
4285+ setCmsAutoSaving ( false ) ;
4286+ }
4287+ } , 300 ) ;
4288+ } ;
4289+
41544290 const handleCmsSelectSection = ( idx : number ) => {
41554291 if ( ! cmsData ) return ;
41564292 setCmsPanelVisible ( true ) ;
@@ -7029,6 +7165,9 @@ function FileExplorerWorkspace({
70297165 onDuplicateVariant = { handleDuplicateVariant }
70307166 onRemoveVariant = { handleRemoveVariant }
70317167 onRemoveAllVariants = { handleRemoveAllVariants }
7168+ onRemoveAllPageVariants = {
7169+ handleRemoveAllPageVariants
7170+ }
70327171 pageVariants = { cmsData ?. pageVariants }
70337172 selectedPageVariant = { cmsSelectedPageVariant }
70347173 onSelectPageVariant = { handleSelectPageVariant }
0 commit comments