17 Commits

Author SHA1 Message Date
Raphire
c79c05f286 Refactor feature handling, update format in Features.json 2026-04-01 21:33:24 +02:00
Raphire
105198e396 Set Topmost property to False in RevertSettingsWindow 2026-03-27 20:42:03 +01:00
Raphire
f8f85ca861 Refactor checkbox style assignment in config window 2026-03-27 20:36:16 +01:00
Raphire
bd16457552 Merge branch 'master' into undo-tweaks 2026-03-27 20:34:41 +01:00
Raphire
ad225cdf9d Adjust margin for revert button 2026-03-23 23:21:08 +01:00
Raphire
2eddbe5638 Refactor/clean up window styles 2026-03-23 21:35:33 +01:00
Raphire
b5c576519b Remove unnecessary parameter handling 2026-03-23 21:08:16 +01:00
Raphire
b1cf364c7d Don't allow undo in combination with deployment-targeted parameters 2026-03-23 21:03:22 +01:00
Raphire
bc8fc1a284 Set default launch mode to CLI for deployment-targeted parameters 2026-03-23 20:56:22 +01:00
Raphire
e9bccccc09 Add error handling for ExecuteAllChanges to improve robustness 2026-03-23 20:47:46 +01:00
Raphire
b0125ddcd2 Refactor undo handling in ExecuteParameter function to improve clarity and error handling 2026-03-23 20:47:32 +01:00
Raphire
c15a18c376 Remove unnecessary call to updateState in Show-RevertSettingsModal function 2026-03-23 00:00:06 +01:00
Raphire
85bdf765e5 Remove unnecessary assignment of SelectedFeatureIds and RestartExplorer in cancel handler 2026-03-22 23:58:21 +01:00
Raphire
bcfed9daff Set RestartExplorer to false by default in Show-RevertSettingsModal function 2026-03-22 23:57:19 +01:00
Raphire
91a9beed0c Also check for required regfiles and assets 2026-03-22 23:48:56 +01:00
Raphire
cfc868ba91 Fix undo when using presets 2026-03-22 23:44:59 +01:00
Raphire
a54c3c6918 Add option to revert previous changes to windows defaults 2026-03-22 22:07:51 +01:00
27 changed files with 1323 additions and 877 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -160,7 +160,6 @@ Below is an overview of the key features and functionality offered by Win11Deblo
- Add all common folders (Desktop, Downloads, etc.) back to 'This PC' in File Explorer. - Add all common folders (Desktop, Downloads, etc.) back to 'This PC' in File Explorer.
- Hide the 3D objects, music or OneDrive folder from the File Explorer navigation pane. - Hide the 3D objects, music or OneDrive folder from the File Explorer navigation pane.
- Hide the 'Include in library', 'Give access to' and 'Share' options from the context menu. - Hide the 'Include in library', 'Give access to' and 'Share' options from the context menu.
- Change drive letter position or visibility in File Explorer.
#### Multi-tasking #### Multi-tasking

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000002

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000004

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000000

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000001

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[hkey_users\default\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000002

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[hkey_users\default\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000004

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[hkey_users\default\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000000

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[hkey_users\default\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000001

View File

@@ -1,4 +0,0 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShowDriveLettersFirst"=dword:00000000

View File

@@ -9,55 +9,6 @@
Background="Transparent" Background="Transparent"
Foreground="{DynamicResource FgColor}"> Foreground="{DynamicResource FgColor}">
<Window.Resources> <Window.Resources>
<!-- CheckBox Style -->
<Style TargetType="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource FgColor}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="4,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Border Background="{TemplateBinding Background}" BorderThickness="0" CornerRadius="4" Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="CheckBoxBorder" Grid.Column="0" Width="18" Height="18" Background="{DynamicResource CheckBoxBgColor}" BorderBrush="{DynamicResource CheckBoxBorderColor}" BorderThickness="1" CornerRadius="4" Margin="0,0,8,0">
<TextBlock x:Name="CheckMark" Text="&#xE73E;" FontFamily="Segoe MDL2 Assets" FontSize="12" Foreground="{DynamicResource ButtonBg}" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed"/>
</Border>
<ContentPresenter Grid.Column="1" VerticalAlignment="Center" Margin="0,0,0,1"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource CheckBoxHoverColor}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckMark" Property="Foreground" Value="White"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonHover}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- CheckBox style for apps panels -->
<Style x:Key="AppsPanelCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="2,3,2,3"/>
</Style>
<!-- Title Bar Button Style --> <!-- Title Bar Button Style -->
<Style x:Key="TitleBarButton" TargetType="Button"> <Style x:Key="TitleBarButton" TargetType="Button">
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>

View File

@@ -180,20 +180,20 @@
<StackPanel x:Name="ButtonPanel" <StackPanel x:Name="ButtonPanel"
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Center"> HorizontalAlignment="Center">
<Button x:Name="ApplyKofiBtn" Width="210" Height="32" <Button x:Name="ApplyKofiBtn"
Width="200" Height="32" Margin="4,0"
Style="{DynamicResource SecondaryButtonStyle}" Style="{DynamicResource SecondaryButtonStyle}"
Margin="0,0,12,0"
AutomationProperties.Name="Support the creator"> AutomationProperties.Name="Support the creator">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="&#xEB52;" FontFamily="Segoe Fluent Icons" FontSize="14" VerticalAlignment="Center" Margin="0,0,8,-1"/> <TextBlock Text="&#xEB52;" FontFamily="Segoe Fluent Icons" FontSize="14" VerticalAlignment="Center" Margin="0,0,8,-1"/>
<TextBlock Text="Support the creator" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/> <TextBlock Text="Support the creator" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="ApplyCloseBtn" Width="100" Height="32" <Button x:Name="ApplyCloseBtn"
Content="Close"
Width="200" Height="32" Margin="4,0"
Style="{DynamicResource PrimaryButtonStyle}" Style="{DynamicResource PrimaryButtonStyle}"
AutomationProperties.Name="Close"> AutomationProperties.Name="Close"/>
<TextBlock Text="Close" VerticalAlignment="Center" FontSize="14" Margin="0,0,0,1"/>
</Button>
</StackPanel> </StackPanel>
</Border> </Border>
</StackPanel> </StackPanel>

View File

@@ -1,22 +1,15 @@
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
Title="Win11Debloat" Title="Win11Debloat"
MinWidth="1130" MinHeight="600" MinWidth="1130" MinHeight="600"
MaxWidth="1400"
ResizeMode="CanResize" ResizeMode="CanResize"
SnapsToDevicePixels="True" SnapsToDevicePixels="True"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
WindowStyle="None" WindowStyle="None"
AllowsTransparency="False" AllowsTransparency="True"
Background="{DynamicResource BgColor}" Background="Transparent"
Foreground="{DynamicResource FgColor}"> Foreground="{DynamicResource FgColor}">
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome ResizeBorderThickness="5"
CaptionHeight="32"
CornerRadius="8"
GlassFrameThickness="0"
UseAeroCaptionButtons="False"/>
</shell:WindowChrome.WindowChrome>
<Window.Resources> <Window.Resources>
<!-- Sort column header hover style --> <!-- Sort column header hover style -->
<Style x:Key="SortHeaderBtnStyle" TargetType="StackPanel"> <Style x:Key="SortHeaderBtnStyle" TargetType="StackPanel">
@@ -343,89 +336,6 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<!-- CheckBox Style -->
<Style TargetType="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource FgColor}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="4,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Border Background="{TemplateBinding Background}" BorderThickness="0" CornerRadius="4" Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="CheckBoxBorder" Grid.Column="0" Width="18" Height="18" Background="{DynamicResource CheckBoxBgColor}" BorderBrush="{DynamicResource CheckBoxBorderColor}" BorderThickness="1" CornerRadius="4" Margin="0,0,8,0">
<Grid>
<TextBlock x:Name="CheckMark" Text="&#xE73E;" FontFamily="Segoe Fluent Icons" FontSize="12" Foreground="{DynamicResource ButtonBg}" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed"/>
<TextBlock x:Name="IndeterminateMark" Text="&#xE738;" FontFamily="Segoe Fluent Icons" FontSize="11" Foreground="{DynamicResource ButtonBg}" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed" Margin="1,1,0,0" />
</Grid>
</Border>
<ContentPresenter Grid.Column="1" VerticalAlignment="Center" Margin="0,0,0,2"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource CheckBoxHoverColor}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckMark" Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible"/>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="Opacity" Value="0.8"/>
<Setter TargetName="IndeterminateMark" Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonDisabled}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
<Setter Property="Foreground" Value="{DynamicResource ButtonTextDisabled}"/>
<Setter Property="Opacity" Value="0.6"/>
<Setter TargetName="CheckMark" Property="Foreground" Value="{DynamicResource ButtonTextDisabled}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonHover}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="{x:Null}"/>
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="Opacity" Value="0.8"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- CheckBox style for feature toggles -->
<Style x:Key="FeatureCheckboxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="-4,-2,-4,10"/>
<Setter Property="Padding" Value="4,2"/>
</Style>
<!-- CheckBox style for apps panels -->
<Style x:Key="AppsPanelCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="2,3,2,3"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<!-- TextBlock style for App ID column in apps table --> <!-- TextBlock style for App ID column in apps table -->
<Style x:Key="AppIdTextStyle" TargetType="TextBlock"> <Style x:Key="AppIdTextStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource AppIdColor}"/> <Setter Property="Foreground" Value="{DynamicResource AppIdColor}"/>
@@ -465,11 +375,6 @@
<Setter Property="Margin" Value="0,1,0,0"/> <Setter Property="Margin" Value="0,1,0,0"/>
</Style> </Style>
<!-- Style for dynamically-created preset checkboxes in the Quick Select popup -->
<Style x:Key="PresetCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="8,4"/>
</Style>
<!-- Progress step indicator fill colors --> <!-- Progress step indicator fill colors -->
<SolidColorBrush x:Key="ProgressActiveColor" Color="#0067c0"/> <SolidColorBrush x:Key="ProgressActiveColor" Color="#0067c0"/>
<SolidColorBrush x:Key="ProgressInactiveColor" Color="#808080"/> <SolidColorBrush x:Key="ProgressInactiveColor" Color="#808080"/>
@@ -608,11 +513,18 @@
</Style> </Style>
</Window.Resources> </Window.Resources>
<Border x:Name="MainBorder" BorderBrush="{DynamicResource BorderColor}" <Border BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1" BorderThickness="1"
CornerRadius="8" CornerRadius="8"
Background="{DynamicResource BgColor}" Background="{DynamicResource BgColor}"
Margin="0"> Margin="25">
<Border.Effect>
<DropShadowEffect Color="Black"
Opacity="0.15"
BlurRadius="20"
ShadowDepth="0"
Direction="0"/>
</Border.Effect>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="32"/> <RowDefinition Height="32"/>
@@ -621,18 +533,18 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Resize Borders --> <!-- Resize Borders -->
<Rectangle x:Name="ResizeLeft" Width="5" HorizontalAlignment="Left" Fill="Transparent" Cursor="SizeWE" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="100" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeLeft" Width="5" HorizontalAlignment="Left" Fill="Transparent" Cursor="SizeWE" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="100"/>
<Rectangle x:Name="ResizeRight" Width="5" HorizontalAlignment="Right" Fill="Transparent" Cursor="SizeWE" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="100" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeRight" Width="5" HorizontalAlignment="Right" Fill="Transparent" Cursor="SizeWE" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="100"/>
<Rectangle x:Name="ResizeTop" Height="5" VerticalAlignment="Top" Fill="Transparent" Cursor="SizeNS" Grid.Row="0" Panel.ZIndex="100" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeTop" Height="5" VerticalAlignment="Top" Fill="Transparent" Cursor="SizeNS" Grid.Row="0" Panel.ZIndex="100"/>
<Rectangle x:Name="ResizeBottom" Height="5" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNS" Grid.Row="2" Panel.ZIndex="100" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeBottom" Height="5" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNS" Grid.Row="2" Panel.ZIndex="100"/>
<Rectangle x:Name="ResizeTopLeft" Width="8" Height="8" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="Transparent" Cursor="SizeNWSE" Grid.Row="0" Panel.ZIndex="101" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeTopLeft" Width="8" Height="8" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="Transparent" Cursor="SizeNWSE" Grid.Row="0" Panel.ZIndex="101"/>
<Rectangle x:Name="ResizeTopRight" Width="8" Height="8" HorizontalAlignment="Right" VerticalAlignment="Top" Fill="Transparent" Cursor="SizeNESW" Grid.Row="0" Panel.ZIndex="101" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeTopRight" Width="8" Height="8" HorizontalAlignment="Right" VerticalAlignment="Top" Fill="Transparent" Cursor="SizeNESW" Grid.Row="0" Panel.ZIndex="101"/>
<Rectangle x:Name="ResizeBottomLeft" Width="8" Height="8" HorizontalAlignment="Left" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNESW" Grid.Row="2" Panel.ZIndex="101" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeBottomLeft" Width="8" Height="8" HorizontalAlignment="Left" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNESW" Grid.Row="2" Panel.ZIndex="101"/>
<Rectangle x:Name="ResizeBottomRight" Width="8" Height="8" HorizontalAlignment="Right" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNWSE" Grid.Row="2" Panel.ZIndex="101" Visibility="Collapsed" IsHitTestVisible="False"/> <Rectangle x:Name="ResizeBottomRight" Width="8" Height="8" HorizontalAlignment="Right" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNWSE" Grid.Row="2" Panel.ZIndex="101"/>
<!-- Custom Title Bar --> <!-- Custom Title Bar -->
<Grid Grid.Row="0" x:Name="TitleBar"> <Grid Grid.Row="0" x:Name="TitleBar">
<Border x:Name="TitleBarBackground" Background="{DynamicResource BgColor}" CornerRadius="8,8,0,0"> <Border Background="{DynamicResource BgColor}" CornerRadius="8,8,0,0">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -645,8 +557,8 @@
Margin="12,0,0,0" Margin="12,0,0,0"
FontSize="12"/> FontSize="12"/>
<StackPanel Grid.Column="1" Orientation="Horizontal"> <StackPanel Grid.Column="1" Orientation="Horizontal">
<Button x:Name="KofiBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="&#xEB52;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Support the creator" AutomationProperties.Name="Support the creator"/> <Button x:Name="KofiBtn" Content="&#xEB52;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Support the creator" AutomationProperties.Name="Support the creator"/>
<Button x:Name="MenuBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="&#xE700;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Options" AutomationProperties.Name="Options"> <Button x:Name="MenuBtn" Content="&#xE700;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Options" AutomationProperties.Name="Options">
<Button.ContextMenu> <Button.ContextMenu>
<ContextMenu x:Name="MainMenu"> <ContextMenu x:Name="MainMenu">
<ContextMenu.Resources> <ContextMenu.Resources>
@@ -695,7 +607,7 @@
</ContextMenu> </ContextMenu>
</Button.ContextMenu> </Button.ContextMenu>
</Button> </Button>
<Button x:Name="CloseBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="&#xE8BB;" Style="{StaticResource CloseButton}" ToolTip="Close" AutomationProperties.Name="Close"/> <Button x:Name="CloseBtn" Content="&#xE8BB;" Style="{StaticResource CloseButton}" ToolTip="Close" AutomationProperties.Name="Close"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Border> </Border>
@@ -776,6 +688,11 @@
</StackPanel> </StackPanel>
</Button> </Button>
</StackPanel> </StackPanel>
<!-- Revert link -->
<Button x:Name="HomeRevertLinkBtn" HorizontalAlignment="Center" Margin="0,11,0,0" Style="{DynamicResource ActionLinkButtonStyle}" AutomationProperties.Name="Revert previous changes">
<TextBlock Text="Revert previous changes" Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
</TabItem> </TabItem>
@@ -1129,12 +1046,12 @@
<!-- Restore Point Option --> <!-- Restore Point Option -->
<StackPanel> <StackPanel>
<CheckBox x:Name="RestorePointCheckBox" Style="{StaticResource FeatureCheckboxStyle}" IsChecked="True" Content="Create a system restore point (Recommended)" AutomationProperties.Name="Create a system restore point (Recommended)"/> <CheckBox x:Name="RestorePointCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" IsChecked="True" Content="Create a system restore point (Recommended)" AutomationProperties.Name="Create a system restore point (Recommended)"/>
</StackPanel> </StackPanel>
<!-- Restart Explorer Option --> <!-- Restart Explorer Option -->
<StackPanel> <StackPanel>
<CheckBox x:Name="RestartExplorerCheckBox" Style="{StaticResource FeatureCheckboxStyle}" Content="Restart the Windows Explorer process to apply all changes immediately" AutomationProperties.Name="Restart the Windows Explorer process to apply all changes immediately"/> <CheckBox x:Name="RestartExplorerCheckBox" Style="{DynamicResource FeatureCheckboxStyle}" Content="Restart the Windows Explorer process to apply all changes immediately" AutomationProperties.Name="Restart the Windows Explorer process to apply all changes immediately"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
@@ -1163,20 +1080,8 @@
<!-- Review & Apply Section --> <!-- Review & Apply Section -->
<StackPanel Grid.Row="1" HorizontalAlignment="Stretch" Background="{DynamicResource BgColor}"> <StackPanel Grid.Row="1" HorizontalAlignment="Stretch" Background="{DynamicResource BgColor}">
<Button x:Name="ReviewChangesBtn" Background="Transparent" BorderThickness="0" Cursor="Hand" HorizontalAlignment="Center" Margin="0,4,0,10" AutomationProperties.Name="Review selected changes"> <Button x:Name="ReviewChangesBtn" Style="{DynamicResource ActionLinkButtonStyle}" HorizontalAlignment="Center" Margin="0,4,0,10" AutomationProperties.Name="Review selected changes">
<Button.Template> <TextBlock Text="Review selected changes" Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
<ControlTemplate TargetType="Button">
<TextBlock x:Name="LinkText" Text="Review selected changes" FontSize="14" Foreground="{DynamicResource ButtonBg}" FontWeight="SemiBold" HorizontalAlignment="Center"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="LinkText" Property="Foreground" Value="{DynamicResource ButtonHover}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="LinkText" Property="Foreground" Value="{DynamicResource ButtonPressed}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button> </Button>
<Button x:Name="DeploymentApplyBtn" Style="{DynamicResource PrimaryButtonStyle}" Width="190" Height="44" HorizontalAlignment="Center" AutomationProperties.Name="Apply Changes"> <Button x:Name="DeploymentApplyBtn" Style="{DynamicResource PrimaryButtonStyle}" Width="190" Height="44" HorizontalAlignment="Center" AutomationProperties.Name="Apply Changes">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center">

View File

@@ -0,0 +1,160 @@
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Revert Changes"
Width="620"
Height="620"
MinWidth="560"
MinHeight="420"
ResizeMode="CanResize"
WindowStartupLocation="CenterOwner"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="False"
ShowInTaskbar="False">
<Window.Resources>
<Style x:Key="RevertItemBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
<Setter Property="Padding" Value="4,6,4,6"/>
</Style>
<Style x:Key="RevertItemRowStyle" TargetType="StackPanel">
<Setter Property="Orientation" Value="Vertical"/>
</Style>
<Style x:Key="RevertItemUndoTextStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource FgColor}"/>
<Setter Property="Opacity" Value="0.75"/>
<Setter Property="Margin" Value="26,-8,0,3"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style x:Key="RevertEmptyTextStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource FgColor}"/>
<Setter Property="Opacity" Value="0.85"/>
<Setter Property="Margin" Value="6,0,6,0"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</Window.Resources>
<Border BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
CornerRadius="8"
Background="{DynamicResource CardBgColor}"
Margin="25">
<Border.Effect>
<DropShadowEffect Color="Black"
Opacity="0.15"
BlurRadius="20"
ShadowDepth="0"
Direction="0"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" x:Name="TitleBar" Height="40" Background="Transparent">
<TextBlock x:Name="TitleText"
Text="Revert Changes"
Foreground="{DynamicResource FgColor}"
FontSize="16"
FontWeight="SemiBold"
VerticalAlignment="Center"
Margin="16,0,0,0"/>
</Grid>
<StackPanel Grid.Row="1" Margin="20,12,1,12">
<TextBlock x:Name="MessageText"
Grid.Column="1"
Text="Select which previously applied tweaks should be reverted to Windows defaults."
TextWrapping="Wrap"
FontSize="14"
LineHeight="20"
Foreground="{DynamicResource FgColor}"
VerticalAlignment="Center"/>
</StackPanel>
<Grid Grid.Row="2" Margin="20,0,20,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Margin="0,0,0,8">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<Button x:Name="RevertSelectAllBtn"
Content="Select All"
Height="30"
Padding="12,0"
Style="{DynamicResource SecondaryButtonStyle}"
Margin="0,0,8,0"
AutomationProperties.Name="Select All"/>
<Button x:Name="RevertClearBtn"
Content="Clear"
Height="30"
Padding="12,0"
Style="{DynamicResource SecondaryButtonStyle}"
AutomationProperties.Name="Clear"/>
</StackPanel>
<TextBlock x:Name="RevertSelectionCount"
Text="0 settings selected"
Foreground="{DynamicResource FgColor}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
DockPanel.Dock="Right"
Margin="0,0,6,0"/>
</DockPanel>
<Border Grid.Row="1"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
CornerRadius="6"
Background="{DynamicResource BgColor}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Padding="8" Margin="0,1,1,1">
<StackPanel x:Name="RevertItemsPanel"/>
</ScrollViewer>
</Border>
<CheckBox x:Name="RevertRestartExplorerCheckBox"
Grid.Row="2"
Content="Restart the Windows Explorer process to apply all changes immediately"
IsChecked="False"
Margin="2,10,0,0"
AutomationProperties.Name="Restart the Windows Explorer process to apply all changes immediately"/>
</Grid>
<!-- Button Panel -->
<Border Grid.Row="3"
Background="{DynamicResource BgColor}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="0,1,0,0"
Padding="16,12"
CornerRadius="0,0,8,8">
<StackPanel x:Name="ButtonPanel"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button x:Name="RevertApplyBtn"
Content="Revert Selected Settings"
Height="32" MinWidth="200" Margin="4,0"
Style="{DynamicResource PrimaryButtonStyle}"
IsEnabled="False"
AutomationProperties.Name="Revert Selected Settings"/>
<Button x:Name="RevertCancelBtn"
Content="Cancel"
Height="32" MinWidth="80" Margin="4,0"
Style="{DynamicResource SecondaryButtonStyle}"
AutomationProperties.Name="Cancel"/>
</StackPanel>
</Border>
</Grid>
</Border>
</Window>

View File

@@ -18,7 +18,7 @@
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4" CornerRadius="4"
Padding="{TemplateBinding Padding}"> Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,1"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
</Setter.Value> </Setter.Value>
@@ -90,6 +90,32 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<!-- Shared link-style button used for text actions like review/revert links -->
<Style x:Key="ActionLinkButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="{DynamicResource ButtonBg}"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{DynamicResource ButtonHover}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="{DynamicResource ButtonPressed}"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- ProgressBar Style --> <!-- ProgressBar Style -->
<Style x:Key="ApplyProgressBarStyle" TargetType="ProgressBar"> <Style x:Key="ApplyProgressBarStyle" TargetType="ProgressBar">
<Setter Property="Background" Value="{DynamicResource ButtonBorderColor}"/> <Setter Property="Background" Value="{DynamicResource ButtonBorderColor}"/>
@@ -125,6 +151,138 @@
<Setter Property="TextAlignment" Value="Center"/> <Setter Property="TextAlignment" Value="Center"/>
</Style> </Style>
<!-- Base CheckBox style used across windows -->
<Style TargetType="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource FgColor}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="4,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Border Background="{TemplateBinding Background}" BorderThickness="0" CornerRadius="4" Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="CheckBoxBorder" Grid.Column="0" Width="18" Height="18" Background="{DynamicResource CheckBoxBgColor}" BorderBrush="{DynamicResource CheckBoxBorderColor}" BorderThickness="1" CornerRadius="4" Margin="0,0,8,0">
<Grid>
<TextBlock x:Name="CheckMark" Text="&#xE73E;" FontFamily="Segoe Fluent Icons" FontSize="12" Foreground="{DynamicResource ButtonBg}" HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0">
<TextBlock.Clip>
<RectangleGeometry x:Name="CheckMarkClip" Rect="0,0,0,16"/>
</TextBlock.Clip>
</TextBlock>
<TextBlock x:Name="IndeterminateMark" Text="&#xE738;" FontFamily="Segoe Fluent Icons" FontSize="11" Foreground="{DynamicResource ButtonBg}" HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0" Margin="1,1,0,0">
<TextBlock.Clip>
<RectangleGeometry x:Name="IndeterminateMarkClip" Rect="0,0,0,16"/>
</TextBlock.Clip>
</TextBlock>
</Grid>
</Border>
<ContentPresenter Grid.Column="1" VerticalAlignment="Center" Margin="0,0,0,2"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource CheckBoxHoverColor}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckMark" Property="Foreground" Value="White"/>
<Setter TargetName="CheckMark" Property="Opacity" Value="1"/>
<Setter TargetName="IndeterminateMark" Property="Opacity" Value="0"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="CheckMark" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" FillBehavior="HoldEnd"/>
<RectAnimation Storyboard.TargetName="CheckMarkClip" Storyboard.TargetProperty="Rect" From="0,0,0,16" To="0,0,16,16" Duration="0:0:0.25" FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<RectAnimation Storyboard.TargetName="CheckMarkClip" Storyboard.TargetProperty="Rect" From="0,0,16,16" To="16,0,0,16" Duration="0:0:0.15" FillBehavior="HoldEnd"/>
<DoubleAnimation Storyboard.TargetName="CheckMark" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.15" FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonBg}"/>
<Setter TargetName="CheckBoxBorder" Property="Opacity" Value="0.8"/>
<Setter TargetName="IndeterminateMark" Property="Foreground" Value="White"/>
<Setter TargetName="IndeterminateMark" Property="Opacity" Value="1"/>
<Setter TargetName="CheckMark" Property="Opacity" Value="0"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="IndeterminateMark" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" FillBehavior="HoldEnd"/>
<RectAnimation Storyboard.TargetName="IndeterminateMarkClip" Storyboard.TargetProperty="Rect" From="0,0,0,16" To="0,0,16,16" Duration="0:0:0.25" FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<RectAnimation Storyboard.TargetName="IndeterminateMarkClip" Storyboard.TargetProperty="Rect" From="0,0,16,16" To="16,0,0,16" Duration="0:0:0.15" FillBehavior="HoldEnd"/>
<DoubleAnimation Storyboard.TargetName="IndeterminateMark" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.15" FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="CheckMark" Property="Opacity" Value="0"/>
<Setter TargetName="IndeterminateMark" Property="Opacity" Value="0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonDisabled}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
<Setter Property="Opacity" Value="0.4"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonHover}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="{x:Null}"/>
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxBorder" Property="Background" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="BorderBrush" Value="{DynamicResource ButtonHover}"/>
<Setter TargetName="CheckBoxBorder" Property="Opacity" Value="0.8"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Shared checkbox style used for feature toggles across windows -->
<Style x:Key="FeatureCheckboxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="-4,-2,-4,10"/>
<Setter Property="Padding" Value="4,2"/>
</Style>
<!-- Shared CheckBox style for app list items -->
<Style x:Key="AppsPanelCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="2,3,2,3"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<!-- Shared CheckBox style for preset picker entries -->
<Style x:Key="PresetCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Margin" Value="8,4"/>
</Style>
<!-- ScrollBar Style --> <!-- ScrollBar Style -->
<Style TargetType="{x:Type ScrollBar}"> <Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>

View File

@@ -1,14 +1,33 @@
# Prints all pending changes that will be made by the script # Prints all pending changes that will be made by the script
function PrintPendingChanges { function PrintPendingChanges {
$skippedParams = @()
$undoChanges = $script:Params.ContainsKey('Undo')
if ($undoChanges) {
Write-Output "Win11Debloat will make the following changes to revert the selected settings to Windows defaults:"
}
else {
Write-Output "Win11Debloat will make the following changes:" Write-Output "Win11Debloat will make the following changes:"
}
if ($script:Params['CreateRestorePoint']) { if ($script:Params['CreateRestorePoint']) {
Write-Output "- $($script:Features['CreateRestorePoint'].Label)" Write-Output "- $($script:Features['CreateRestorePoint'].Action)"
} }
foreach ($parameterName in $script:Params.Keys) { foreach ($parameterName in $script:Params.Keys) {
if ($script:ControlParams -contains $parameterName) { if ($script:ControlParams -contains $parameterName) {
continue continue
} }
if ($parameterName -eq 'Apps' -or $parameterName -eq 'CreateRestorePoint') {
continue
}
if ($undoChanges) {
$undoFeature = GetUndoFeatureForParam -paramKey $parameterName
if (-not $undoFeature) {
$skippedParams += $parameterName
continue
}
}
# Print parameter description # Print parameter description
switch ($parameterName) { switch ($parameterName) {
@@ -46,9 +65,18 @@ function PrintPendingChanges {
} }
default { default {
if ($script:Features -and $script:Features.ContainsKey($parameterName)) { if ($script:Features -and $script:Features.ContainsKey($parameterName)) {
$action = $script:Features[$parameterName].Action $message = if ($undoChanges -and $script:Features[$parameterName].UndoAction) {
$message = $script:Features[$parameterName].Label $script:Features[$parameterName].UndoAction
Write-Output "- $action $message" }
else {
$script:Features[$parameterName].Action
}
if ($message) {
Write-Output "- $message"
}
else {
Write-Output "- $parameterName"
}
} }
else { else {
# Fallback: show the parameter name if no feature description is available # Fallback: show the parameter name if no feature description is available
@@ -59,6 +87,16 @@ function PrintPendingChanges {
} }
} }
if ($undoChanges -and $skippedParams.Count -gt 0) {
Write-Output ""
Write-Output "The following changes cannot be automatically undone and will be skipped:"
$uniqueSkipped = $skippedParams | Sort-Object -Unique
foreach ($skippedParam in $uniqueSkipped) {
Write-Output "- $($script:Features[$skippedParam].Action)"
}
}
Write-Output "" Write-Output ""
Write-Output "" Write-Output ""
Write-Output "Press enter to execute the script or press CTRL+C to quit..." Write-Output "Press enter to execute the script or press CTRL+C to quit..."

View File

@@ -6,13 +6,35 @@ function ExecuteParameter {
[string]$paramKey [string]$paramKey
) )
# Check if this feature has metadata in Features.json # Check if this feature exists in Features.json
$feature = $null $feature = $null
if ($script:Features.ContainsKey($paramKey)) { if ($script:Features.ContainsKey($paramKey)) {
$feature = $script:Features[$paramKey] $feature = $script:Features[$paramKey]
} }
# If feature has RegistryKey and ApplyText, use dynamic ImportRegistryFile # Check if undo is requested and if this feature supports undo
$undoChanges = $script:Params.ContainsKey('Undo')
if ($undoChanges) {
$undoFeature = GetUndoFeatureForParam -paramKey $paramKey
if ($null -eq $undoFeature) {
# This parameter doesn't support undo, so skip it
return
}
$undoRegFile = $undoFeature.RegistryUndoKey
$undoFolderPath = Join-Path $script:RegfilesPath (Join-Path 'Undo' $undoRegFile)
if (Test-Path $undoFolderPath) {
$undoRegFile = Join-Path 'Undo' $undoRegFile
}
ImportRegistryFile "> $($undoFeature.UndoText)" $undoRegFile
return
}
# If feature has RegistryKey and ApplyText, dynamically import the registry file for this feature
if ($feature -and $feature.RegistryKey -and $feature.ApplyText) { if ($feature -and $feature.RegistryKey -and $feature.ApplyText) {
ImportRegistryFile "> $($feature.ApplyText)" $feature.RegistryKey ImportRegistryFile "> $($feature.ApplyText)" $feature.RegistryKey
@@ -139,14 +161,37 @@ function ExecuteParameter {
# Executes all selected parameters/features # Executes all selected parameters/features
function ExecuteAllChanges { function ExecuteAllChanges {
# Build list of actionable parameters (skip control params and data-only params) # Build list of actionable parameters (skip control params and data-only params)
$undoChanges = $script:Params.ContainsKey('Undo')
$actionableKeys = @() $actionableKeys = @()
$paramsToRemove = @()
foreach ($paramKey in $script:Params.Keys) { foreach ($paramKey in $script:Params.Keys) {
if ($script:ControlParams -contains $paramKey) { continue } if ($script:ControlParams -contains $paramKey) { continue }
if ($paramKey -eq 'Apps') { continue } if ($paramKey -eq 'Apps') { continue }
if ($paramKey -eq 'CreateRestorePoint') { continue } if ($paramKey -eq 'CreateRestorePoint') { continue }
if ($undoChanges) {
$undoFeature = GetUndoFeatureForParam -paramKey $paramKey
if (-not $undoFeature) {
$paramsToRemove += $paramKey
continue
}
}
$actionableKeys += $paramKey $actionableKeys += $paramKey
} }
if ($undoChanges -and $paramsToRemove.Count -gt 0) {
foreach ($paramKey in ($paramsToRemove | Sort-Object -Unique)) {
if ($script:Params.ContainsKey($paramKey)) {
$null = $script:Params.Remove($paramKey)
}
}
}
if ($undoChanges -and $actionableKeys.Count -eq 0) {
throw "Undo was requested but none of the selected parameters support undo. No changes were reverted."
}
$totalSteps = $actionableKeys.Count $totalSteps = $actionableKeys.Count
if ($script:Params.ContainsKey("CreateRestorePoint")) { $totalSteps++ } if ($script:Params.ContainsKey("CreateRestorePoint")) { $totalSteps++ }
$currentStep = 0 $currentStep = 0
@@ -174,16 +219,13 @@ function ExecuteAllChanges {
$stepName = $paramKey $stepName = $paramKey
if ($script:Features.ContainsKey($paramKey)) { if ($script:Features.ContainsKey($paramKey)) {
$feature = $script:Features[$paramKey] $feature = $script:Features[$paramKey]
if ($feature.ApplyText) { if ($undoChanges -and $feature.UndoText) {
# Prefer explicit ApplyText when provided $stepName = $feature.UndoText
$stepName = $feature.ApplyText
} elseif ($feature.Label) {
# Fallback: construct a name from Action and Label, or just Label
if ($feature.Action) {
$stepName = "$($feature.Action) $($feature.Label)"
} else {
$stepName = $feature.Label
} }
elseif ($feature.ApplyText) {
$stepName = $feature.ApplyText
} elseif ($feature.Action) {
$stepName = $feature.Action
} }
} }

View File

@@ -10,7 +10,7 @@ function RestartExplorer {
foreach ($paramKey in $script:Params.Keys) { foreach ($paramKey in $script:Params.Keys) {
if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) { if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) {
$feature = $script:Features[$paramKey] $feature = $script:Features[$paramKey]
Write-Host "Warning: '$($feature.Action) $($feature.Label)' requires a reboot to take full effect" -ForegroundColor Yellow Write-Host "Warning: '$($feature.Action)' requires a reboot to take full effect" -ForegroundColor Yellow
} }
} }

View File

@@ -162,7 +162,7 @@ function Show-ApplyModal {
foreach ($paramKey in $script:Params.Keys) { foreach ($paramKey in $script:Params.Keys) {
if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) { if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) {
$feature = $script:Features[$paramKey] $feature = $script:Features[$paramKey]
$rebootFeatures += "$($feature.Action) $($feature.Label)" $rebootFeatures += $feature.Action
} }
} }
@@ -179,7 +179,7 @@ function Show-ApplyModal {
$applyRebootPanel.Visibility = 'Visible' $applyRebootPanel.Visibility = 'Visible'
} }
else { else {
$script:ApplyCompletionMessageEl.Text = "Your clean system is ready. Thanks for using Win11Debloat!" $script:ApplyCompletionMessageEl.Text = "Your system is ready. Thanks for using Win11Debloat!"
} }
} }
} }

View File

@@ -44,14 +44,6 @@ function Show-ImportExportConfigWindow {
$dlg.Owner = $Owner $dlg.Owner = $Owner
SetWindowThemeResources -window $dlg -usesDarkMode $UsesDarkMode SetWindowThemeResources -window $dlg -usesDarkMode $UsesDarkMode
# Copy the CheckBox default style from the main window so checkboxes get the themed template
try {
$mainCheckBoxStyle = $Owner.FindResource([type][System.Windows.Controls.CheckBox])
if ($mainCheckBoxStyle) {
$dlg.Resources.Add([type][System.Windows.Controls.CheckBox], $mainCheckBoxStyle)
}
} catch { }
# Populate named elements # Populate named elements
$dlg.Title = $Title $dlg.Title = $Title
$dlg.FindName('TitleText').Text = $Title $dlg.FindName('TitleText').Text = $Title
@@ -70,6 +62,7 @@ function Show-ImportExportConfigWindow {
$cb.Margin = [System.Windows.Thickness]::new(0,0,0,8) $cb.Margin = [System.Windows.Thickness]::new(0,0,0,8)
$cb.FontSize = 14 $cb.FontSize = 14
$cb.Foreground = $dlg.FindResource('FgColor') $cb.Foreground = $dlg.FindResource('FgColor')
$cb.Style = $window.Resources["AppsPanelCheckBoxStyle"]
if ($DisabledCategories -contains $cat) { if ($DisabledCategories -contains $cat) {
$cb.IsChecked = $false $cb.IsChecked = $false
$cb.IsEnabled = $false $cb.IsEnabled = $false

View File

@@ -19,8 +19,7 @@ function Show-MainWindow {
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
# Get named elements # Get named elements
$mainBorder = $window.FindName('MainBorder') $titleBar = $window.FindName('TitleBar')
$titleBarBackground = $window.FindName('TitleBarBackground')
$kofiBtn = $window.FindName('KofiBtn') $kofiBtn = $window.FindName('KofiBtn')
$menuBtn = $window.FindName('MenuBtn') $menuBtn = $window.FindName('MenuBtn')
$closeBtn = $window.FindName('CloseBtn') $closeBtn = $window.FindName('CloseBtn')
@@ -31,86 +30,10 @@ function Show-MainWindow {
$importConfigBtn = $window.FindName('ImportConfigBtn') $importConfigBtn = $window.FindName('ImportConfigBtn')
$exportConfigBtn = $window.FindName('ExportConfigBtn') $exportConfigBtn = $window.FindName('ExportConfigBtn')
$windowStateNormal = [System.Windows.WindowState]::Normal # Title bar event handlers
$windowStateMaximized = [System.Windows.WindowState]::Maximized $titleBar.Add_MouseLeftButtonDown({
$normalWindowShadow = $mainBorder.Effect if ($_.OriginalSource -is [System.Windows.Controls.Grid] -or $_.OriginalSource -is [System.Windows.Controls.Border] -or $_.OriginalSource -is [System.Windows.Controls.TextBlock]) {
$windowResizeBorder = [System.Windows.SystemParameters]::WindowResizeBorderThickness $window.DragMove()
$maximizedChromeInset = [System.Windows.Thickness]::new($windowResizeBorder.Left, $windowResizeBorder.Top, $windowResizeBorder.Right, 0)
$initialNormalMaxWidth = 1400.0
$convertScreenPixelsToDip = {
param(
[double]$width,
[double]$height
)
$source = [System.Windows.PresentationSource]::FromVisual($window)
if ($null -eq $source -or $null -eq $source.CompositionTarget) {
return [System.Windows.Size]::new($width, $height)
}
$topLeft = $source.CompositionTarget.TransformFromDevice.Transform([System.Windows.Point]::new(0, 0))
$bottomRight = $source.CompositionTarget.TransformFromDevice.Transform([System.Windows.Point]::new($width, $height))
return [System.Windows.Size]::new($bottomRight.X - $topLeft.X, $bottomRight.Y - $topLeft.Y)
}
$getWindowScreen = {
$hwnd = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle
if ($hwnd -eq [IntPtr]::Zero) {
return $null
}
return [System.Windows.Forms.Screen]::FromHandle($hwnd)
}
$updateWindowChrome = {
if ($window.WindowState -eq $windowStateMaximized) {
$mainBorder.Margin = $maximizedChromeInset
$mainBorder.BorderThickness = [System.Windows.Thickness]::new(1)
$mainBorder.CornerRadius = [System.Windows.CornerRadius]::new(0)
$mainBorder.Effect = $null
$titleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(0)
}
else {
$mainBorder.Margin = [System.Windows.Thickness]::new(0)
$mainBorder.BorderThickness = [System.Windows.Thickness]::new(1)
$mainBorder.CornerRadius = [System.Windows.CornerRadius]::new(8)
$mainBorder.Effect = $normalWindowShadow
$titleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(8, 8, 0, 0)
}
}
$updateMaximizedBounds = {
& $updateWindowChrome
}
$applyInitialWindowSize = {
if ($window.WindowState -ne $windowStateNormal) {
return
}
$screen = & $getWindowScreen
if ($null -eq $screen) {
return
}
$workingAreaDip = & $convertScreenPixelsToDip $screen.WorkingArea.Width $screen.WorkingArea.Height
$window.Width = [Math]::Min($initialNormalMaxWidth, $workingAreaDip.Width)
$window.Left = $screen.WorkingArea.Left + (($workingAreaDip.Width - $window.Width) / 2)
}
$window.Add_SourceInitialized({
& $applyInitialWindowSize
& $updateMaximizedBounds
})
$window.Add_StateChanged({
& $updateMaximizedBounds
})
$window.Add_LocationChanged({
if ($window.WindowState -eq $windowStateMaximized) {
& $updateMaximizedBounds
} }
}) })
@@ -171,6 +94,135 @@ function Show-MainWindow {
$script:CancelRequested = $true $script:CancelRequested = $true
}) })
# Implement window resize functionality
$resizeLeft = $window.FindName('ResizeLeft')
$resizeRight = $window.FindName('ResizeRight')
$resizeTop = $window.FindName('ResizeTop')
$resizeBottom = $window.FindName('ResizeBottom')
$resizeTopLeft = $window.FindName('ResizeTopLeft')
$resizeTopRight = $window.FindName('ResizeTopRight')
$resizeBottomLeft = $window.FindName('ResizeBottomLeft')
$resizeBottomRight = $window.FindName('ResizeBottomRight')
$script:resizing = $false
$script:resizeEdges = $null
$script:resizeStart = $null
$script:windowStart = $null
$script:resizeElement = $null
$resizeHandler = {
param($sender, $e)
$script:resizing = $true
$script:resizeElement = $sender
$script:resizeStart = [System.Windows.Forms.Cursor]::Position
$script:windowStart = @{
Left = $window.Left
Top = $window.Top
Width = $window.ActualWidth
Height = $window.ActualHeight
}
# Parse direction tag into edge flags for cleaner resize logic
$direction = $sender.Tag
$script:resizeEdges = @{
Left = $direction -match 'Left'
Right = $direction -match 'Right'
Top = $direction -match 'Top'
Bottom = $direction -match 'Bottom'
}
$sender.CaptureMouse()
$e.Handled = $true
}
$moveHandler = {
param($sender, $e)
if (-not $script:resizing) { return }
$current = [System.Windows.Forms.Cursor]::Position
$deltaX = $current.X - $script:resizeStart.X
$deltaY = $current.Y - $script:resizeStart.Y
# Handle horizontal resize
if ($script:resizeEdges.Left) {
$newWidth = [Math]::Max($window.MinWidth, $script:windowStart.Width - $deltaX)
if ($newWidth -ne $window.Width) {
$window.Left = $script:windowStart.Left + ($script:windowStart.Width - $newWidth)
$window.Width = $newWidth
}
}
elseif ($script:resizeEdges.Right) {
$window.Width = [Math]::Max($window.MinWidth, $script:windowStart.Width + $deltaX)
}
# Handle vertical resize
if ($script:resizeEdges.Top) {
$newHeight = [Math]::Max($window.MinHeight, $script:windowStart.Height - $deltaY)
if ($newHeight -ne $window.Height) {
$window.Top = $script:windowStart.Top + ($script:windowStart.Height - $newHeight)
$window.Height = $newHeight
}
}
elseif ($script:resizeEdges.Bottom) {
$window.Height = [Math]::Max($window.MinHeight, $script:windowStart.Height + $deltaY)
}
$e.Handled = $true
}
$releaseHandler = {
param($sender, $e)
if ($script:resizing -and $script:resizeElement) {
$script:resizing = $false
$script:resizeEdges = $null
$script:resizeElement.ReleaseMouseCapture()
$script:resizeElement = $null
$e.Handled = $true
}
}
# Set tags and add event handlers for resize borders
$resizeLeft.Tag = 'Left'
$resizeLeft.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeLeft.Add_MouseMove($moveHandler)
$resizeLeft.Add_MouseLeftButtonUp($releaseHandler)
$resizeRight.Tag = 'Right'
$resizeRight.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeRight.Add_MouseMove($moveHandler)
$resizeRight.Add_MouseLeftButtonUp($releaseHandler)
$resizeTop.Tag = 'Top'
$resizeTop.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeTop.Add_MouseMove($moveHandler)
$resizeTop.Add_MouseLeftButtonUp($releaseHandler)
$resizeBottom.Tag = 'Bottom'
$resizeBottom.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeBottom.Add_MouseMove($moveHandler)
$resizeBottom.Add_MouseLeftButtonUp($releaseHandler)
$resizeTopLeft.Tag = 'TopLeft'
$resizeTopLeft.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeTopLeft.Add_MouseMove($moveHandler)
$resizeTopLeft.Add_MouseLeftButtonUp($releaseHandler)
$resizeTopRight.Tag = 'TopRight'
$resizeTopRight.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeTopRight.Add_MouseMove($moveHandler)
$resizeTopRight.Add_MouseLeftButtonUp($releaseHandler)
$resizeBottomLeft.Tag = 'BottomLeft'
$resizeBottomLeft.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeBottomLeft.Add_MouseMove($moveHandler)
$resizeBottomLeft.Add_MouseLeftButtonUp($releaseHandler)
$resizeBottomRight.Tag = 'BottomRight'
$resizeBottomRight.Add_PreviewMouseLeftButtonDown($resizeHandler)
$resizeBottomRight.Add_MouseMove($moveHandler)
$resizeBottomRight.Add_MouseLeftButtonUp($releaseHandler)
# Integrated App Selection UI # Integrated App Selection UI
$appsPanel = $window.FindName('AppSelectionPanel') $appsPanel = $window.FindName('AppSelectionPanel')
$onlyInstalledAppsBox = $window.FindName('OnlyInstalledAppsBox') $onlyInstalledAppsBox = $window.FindName('OnlyInstalledAppsBox')
@@ -190,32 +242,6 @@ function Show-MainWindow {
$presetsArrow = $window.FindName('PresetsArrow') $presetsArrow = $window.FindName('PresetsArrow')
$clearAppSelectionBtn = $window.FindName('ClearAppSelectionBtn') $clearAppSelectionBtn = $window.FindName('ClearAppSelectionBtn')
function NormalizeCheckboxState {
param([System.Windows.Controls.CheckBox]$checkBox)
$isChecked = ($checkBox.IsChecked -eq $true)
if ($null -eq $checkBox.IsChecked) {
$checkBox.IsChecked = $false
return $false
}
return $isChecked
}
function AnimatePresetsArrow {
param([double]$angle)
$animation = New-Object System.Windows.Media.Animation.DoubleAnimation
$animation.To = $angle
$animation.Duration = [System.Windows.Duration]::new([System.TimeSpan]::FromMilliseconds(200))
$ease = New-Object System.Windows.Media.Animation.CubicEase
$ease.EasingMode = 'EaseOut'
$animation.EasingFunction = $ease
$presetsArrow.RenderTransform.BeginAnimation([System.Windows.Media.RotateTransform]::AngleProperty, $animation)
}
# Load JSON-defined presets and build dynamic preset checkboxes # Load JSON-defined presets and build dynamic preset checkboxes
$script:JsonPresetCheckboxes = @() $script:JsonPresetCheckboxes = @()
foreach ($preset in (LoadAppPresetsFromJson)) { foreach ($preset in (LoadAppPresetsFromJson)) {
@@ -230,7 +256,8 @@ function Show-MainWindow {
$checkbox.Add_Click({ $checkbox.Add_Click({
if ($script:UpdatingPresets) { return } if ($script:UpdatingPresets) { return }
$check = NormalizeCheckboxState -checkBox $this $check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
$presetIds = $this.PresetAppIds $presetIds = $this.PresetAppIds
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $presetIds -contains $_ }).Count -gt 0 }.GetNewClosure() -Check $check ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $presetIds -contains $_ }).Count -gt 0 }.GetNewClosure() -Check $check
}) })
@@ -419,7 +446,7 @@ function Show-MainWindow {
# Dynamically builds Tweaks UI from Features.json # Dynamically builds Tweaks UI from Features.json
function BuildDynamicTweaks { function BuildDynamicTweaks {
$featuresJson = LoadJsonFile -filePath $script:FeaturesFilePath -expectedVersion "1.0" $featuresJson = LoadJsonFile -filePath $script:FeaturesFilePath -expectedVersion $script:FeaturesConfigVersion
if (-not $featuresJson) { if (-not $featuresJson) {
Show-MessageBox -Message "Unable to load Features.json file!" -Title "Error" -Button 'OK' -Icon 'Error' | Out-Null Show-MessageBox -Message "Unable to load Features.json file!" -Title "Error" -Button 'OK' -Icon 'Error' | Out-Null
@@ -563,8 +590,8 @@ function Show-MainWindow {
$helpBtn.Tag = (GetWikiUrlForCategory -category $categoryName) $helpBtn.Tag = (GetWikiUrlForCategory -category $categoryName)
$helpBtn.Style = $window.Resources['CategoryHelpLinkButtonStyle'] $helpBtn.Style = $window.Resources['CategoryHelpLinkButtonStyle']
$helpBtn.Add_Click({ $helpBtn.Add_Click({
param($button, $e) param($sender, $e)
if ($button.Tag) { Start-Process $button.Tag } if ($sender.Tag) { Start-Process $sender.Tag }
}) })
$headerRow.Children.Add($helpBtn) | Out-Null $headerRow.Children.Add($helpBtn) | Out-Null
@@ -682,7 +709,7 @@ function Show-MainWindow {
if ($feature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($feature.FeatureId -match '^Enable') { $opt = 'Enable' } if ($feature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($feature.FeatureId -match '^Enable') { $opt = 'Enable' }
$items = @('No Change', $opt) $items = @('No Change', $opt)
$comboName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]','' $comboName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]',''
$combo = CreateLabeledCombo -parent $panel -labelText ($feature.Action + ' ' + $feature.Label) -comboName $comboName -items $items $combo = CreateLabeledCombo -parent $panel -labelText $feature.Action -comboName $comboName -items $items
# attach tooltip from Features.json if present # attach tooltip from Features.json if present
if ($feature.ToolTip) { if ($feature.ToolTip) {
$tipBlock = New-Object System.Windows.Controls.TextBlock $tipBlock = New-Object System.Windows.Controls.TextBlock
@@ -694,7 +721,7 @@ function Show-MainWindow {
try { $lblBorderObj = $window.FindName("$comboName`_LabelBorder") } catch {} try { $lblBorderObj = $window.FindName("$comboName`_LabelBorder") } catch {}
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock } if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
} }
$script:UiControlMappings[$comboName] = @{ Type='feature'; FeatureId = $feature.FeatureId; Action = $feature.Action; Label = $feature.Label } $script:UiControlMappings[$comboName] = @{ Type='feature'; FeatureId = $feature.FeatureId; Action = $feature.Action }
} }
} }
} }
@@ -702,7 +729,7 @@ function Show-MainWindow {
# Build a feature-label lookup so GenerateOverview can resolve feature IDs without reloading JSON # Build a feature-label lookup so GenerateOverview can resolve feature IDs without reloading JSON
$script:FeatureLabelLookup = @{} $script:FeatureLabelLookup = @{}
foreach ($f in $featuresJson.Features) { foreach ($f in $featuresJson.Features) {
$script:FeatureLabelLookup[$f.FeatureId] = $f.Action + ' ' + $f.Label $script:FeatureLabelLookup[$f.FeatureId] = $f.Action
} }
} }
@@ -891,19 +918,29 @@ function Show-MainWindow {
# Animate arrow when popup opens/closes, and lazily update preset states # Animate arrow when popup opens/closes, and lazily update preset states
$presetsPopup.Add_Opened({ $presetsPopup.Add_Opened({
UpdatePresetStates UpdatePresetStates
AnimatePresetsArrow -angle 180 $animation = New-Object System.Windows.Media.Animation.DoubleAnimation
$animation.To = 180
$animation.Duration = [System.Windows.Duration]::new([System.TimeSpan]::FromMilliseconds(200))
$animation.EasingFunction = New-Object System.Windows.Media.Animation.CubicEase
$animation.EasingFunction.EasingMode = 'EaseOut'
$presetsArrow.RenderTransform.BeginAnimation([System.Windows.Media.RotateTransform]::AngleProperty, $animation)
}) })
$presetsPopup.Add_Closed({ $presetsPopup.Add_Closed({
AnimatePresetsArrow -angle 0 $animation = New-Object System.Windows.Media.Animation.DoubleAnimation
$animation.To = 0
$animation.Duration = [System.Windows.Duration]::new([System.TimeSpan]::FromMilliseconds(200))
$animation.EasingFunction = New-Object System.Windows.Media.Animation.CubicEase
$animation.EasingFunction.EasingMode = 'EaseOut'
$presetsArrow.RenderTransform.BeginAnimation([System.Windows.Media.RotateTransform]::AngleProperty, $animation)
$presetsBtn.IsChecked = $false $presetsBtn.IsChecked = $false
}) })
# Close popup when clicking anywhere outside the popup or the presets button. # Close popup when clicking anywhere outside the popup or the presets button.
$window.Add_PreviewMouseDown({ $window.Add_PreviewMouseDown({
if (-not $presetsPopup.IsOpen) { return } if (-not $presetsPopup.IsOpen) { return }
if ($null -ne $presetsPopup.Child -and $presetsPopup.Child.IsMouseOver) { return } if ($presetsPopup.Child -ne $null -and $presetsPopup.Child.IsMouseOver) { return }
$src = $_.OriginalSource -as [System.Windows.DependencyObject] $src = $_.OriginalSource -as [System.Windows.DependencyObject]
if ($null -ne $src) { if ($src -ne $null) {
$inBtn = $presetsBtn.IsAncestorOf($src) -or [System.Object]::ReferenceEquals($presetsBtn, $src) $inBtn = $presetsBtn.IsAncestorOf($src) -or [System.Object]::ReferenceEquals($presetsBtn, $src)
if (-not $inBtn) { $presetsPopup.IsOpen = $false } if (-not $inBtn) { $presetsPopup.IsOpen = $false }
} }
@@ -923,7 +960,8 @@ function Show-MainWindow {
# Preset: Default selection # Preset: Default selection
$presetDefaultApps.Add_Click({ $presetDefaultApps.Add_Click({
if ($script:UpdatingPresets) { return } if ($script:UpdatingPresets) { return }
$check = NormalizeCheckboxState -checkBox $this $check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
ApplyPresetToApps -MatchFilter { param($c) $c.SelectedByDefault -eq $true } -Check $check ApplyPresetToApps -MatchFilter { param($c) $c.SelectedByDefault -eq $true } -Check $check
}) })
@@ -1040,7 +1078,7 @@ function Show-MainWindow {
}) })
$appSearchBox.Add_KeyDown({ $appSearchBox.Add_KeyDown({
param($sourceControl, $e) param($sender, $e)
if ($e.Key -eq [System.Windows.Input.Key]::Enter -and $script:AppSearchMatches.Count -gt 0) { if ($e.Key -eq [System.Windows.Input.Key]::Enter -and $script:AppSearchMatches.Count -gt 0) {
# Reset background of current active match # Reset background of current active match
$script:AppSearchMatches[$script:AppSearchMatchIndex].Background = $window.Resources["SearchHighlightColor"] $script:AppSearchMatches[$script:AppSearchMatchIndex].Background = $window.Resources["SearchHighlightColor"]
@@ -1163,7 +1201,7 @@ function Show-MainWindow {
# Add Ctrl+F keyboard shortcut to focus search box on current tab # Add Ctrl+F keyboard shortcut to focus search box on current tab
$window.Add_KeyDown({ $window.Add_KeyDown({
param($sourceControl, $e) param($sender, $e)
# Check if Ctrl+F was pressed # Check if Ctrl+F was pressed
if ($e.Key -eq [System.Windows.Input.Key]::F -and if ($e.Key -eq [System.Windows.Input.Key]::F -and
@@ -1392,13 +1430,13 @@ function Show-MainWindow {
if ($userSelectionCombo.SelectedIndex -ne 2) { if ($userSelectionCombo.SelectedIndex -ne 2) {
$appRemovalScopeCombo.IsEnabled = $true $appRemovalScopeCombo.IsEnabled = $true
} }
$appRemovalScopeSection.Opacity = 1.0 $appRemovalScopeSection.Visibility = 'Visible'
UpdateAppRemovalScopeDescription UpdateAppRemovalScopeDescription
} }
else { else {
# Disable app removal scope selection when no apps selected # Disable app removal scope selection when no apps selected
$appRemovalScopeCombo.IsEnabled = $false $appRemovalScopeCombo.IsEnabled = $false
$appRemovalScopeSection.Opacity = 0.5 $appRemovalScopeSection.Visibility = 'Collapsed'
$appRemovalScopeDescription.Text = "No apps selected for removal." $appRemovalScopeDescription.Text = "No apps selected for removal."
} }
@@ -1428,8 +1466,7 @@ function Show-MainWindow {
} }
elseif ($mapping.Type -eq 'feature') { elseif ($mapping.Type -eq 'feature') {
$label = $script:FeatureLabelLookup[$mapping.FeatureId] $label = $script:FeatureLabelLookup[$mapping.FeatureId]
if (-not $label) { $label = $mapping.Action + ' ' + $mapping.Label } if ($label) { $changesList += $label }
$changesList += $label
} }
} }
} }
@@ -1473,6 +1510,44 @@ function Show-MainWindow {
UpdateNavigationButtons UpdateNavigationButtons
}) })
# Handle Home Revert link button
$homeRevertLinkBtn = $window.FindName('HomeRevertLinkBtn')
if ($homeRevertLinkBtn) {
if (-not (Test-Path $script:SavedSettingsFilePath)) {
$homeRevertLinkBtn.Visibility = 'Collapsed'
}
$homeRevertLinkBtn.Add_Click({
$savedSettings = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
if (-not $savedSettings -or -not $savedSettings.Settings) {
return
}
$revertSelection = Show-RevertSettingsModal -Owner $window -LastUsedSettings $savedSettings
$selectedFeatureIds = @($revertSelection.SelectedFeatureIds)
$shouldRestartExplorer = ($revertSelection.RestartExplorer -eq $true)
if (-not $selectedFeatureIds -or $selectedFeatureIds.Count -eq 0) {
return
}
AddParameter 'Undo'
foreach ($featureId in $selectedFeatureIds) {
if ($script:Features.ContainsKey($featureId)) {
$feature = $script:Features[$featureId]
if ($feature.RegistryUndoKey -and $feature.UndoAction) {
AddParameter $featureId
}
}
}
Show-ApplyModal -Owner $window -RestartExplorer $shouldRestartExplorer
$window.Close()
})
}
# Handle Home Default Mode button - apply defaults and navigate directly to overview # Handle Home Default Mode button - apply defaults and navigate directly to overview
$homeDefaultModeBtn = $window.FindName('HomeDefaultModeBtn') $homeDefaultModeBtn = $window.FindName('HomeDefaultModeBtn')
$homeDefaultModeBtn.Add_Click({ $homeDefaultModeBtn.Add_Click({
@@ -1748,7 +1823,8 @@ function Show-MainWindow {
$presetLastUsed.Add_Click({ $presetLastUsed.Add_Click({
if ($script:UpdatingPresets) { return } if ($script:UpdatingPresets) { return }
$check = NormalizeCheckboxState -checkBox $this $check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 } -Check $check ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 } -Check $check
}) })
} }
@@ -1783,12 +1859,5 @@ function Show-MainWindow {
} }
# Show the window # Show the window
$frame = [System.Windows.Threading.DispatcherFrame]::new() return $window.ShowDialog()
$window.Add_Closed({
$frame.Continue = $false
})
$window.Show() | Out-Null
[System.Windows.Threading.Dispatcher]::PushFrame($frame)
return $null
} }

View File

@@ -0,0 +1,198 @@
function Show-RevertSettingsModal {
param (
[Parameter(Mandatory=$false)]
[System.Windows.Window]$Owner = $null,
[Parameter(Mandatory=$true)]
$LastUsedSettings
)
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
# Determine owner window
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
# Show overlay if owner window exists
$overlay = $null
$overlayWasAlreadyVisible = $false
if ($ownerWindow) {
try {
$overlay = $ownerWindow.FindName('ModalOverlay')
if ($overlay) {
$overlayWasAlreadyVisible = ($overlay.Visibility -eq 'Visible')
if (-not $overlayWasAlreadyVisible) {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Visible' })
}
}
}
catch { }
}
# Load XAML from file
$xaml = Get-Content -Path $script:RevertSettingsWindowSchema -Raw
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
try {
$revertWindow = [System.Windows.Markup.XamlReader]::Load($reader)
}
finally {
$reader.Close()
}
if ($ownerWindow) {
try {
$revertWindow.Owner = $ownerWindow
}
catch { }
}
SetWindowThemeResources -window $revertWindow -usesDarkMode $usesDarkMode
$itemsPanel = $revertWindow.FindName('RevertItemsPanel')
$countText = $revertWindow.FindName('RevertSelectionCount')
$selectAllBtn = $revertWindow.FindName('RevertSelectAllBtn')
$clearBtn = $revertWindow.FindName('RevertClearBtn')
$applyBtn = $revertWindow.FindName('RevertApplyBtn')
$cancelBtn = $revertWindow.FindName('RevertCancelBtn')
$restartExplorerCheckbox = $revertWindow.FindName('RevertRestartExplorerCheckBox')
$featureCheckboxStyle = $revertWindow.FindResource('FeatureCheckboxStyle')
$restartExplorerCheckbox.Style = $featureCheckboxStyle
$entryCheckboxes = @()
foreach ($setting in $LastUsedSettings.Settings) {
if ($setting.Value -ne $true) { continue }
if ($setting.Name -eq 'Apps' -or $setting.Name -eq 'RemoveApps' -or $setting.Name -eq 'CreateRestorePoint') { continue }
$feature = $null
if ($script:Features.ContainsKey($setting.Name)) {
$feature = $script:Features[$setting.Name]
}
$undoFeature = GetUndoFeatureForParam -paramKey $setting.Name
$label = $setting.Name
if ($feature -and $feature.Action) {
$label = $feature.Action
}
$undoLabel = if ($undoFeature -and $undoFeature.UndoAction) {
$undoFeature.UndoAction
} else {
'No revert action available'
}
$canUndo = ($null -ne $undoFeature)
$itemBorder = New-Object System.Windows.Controls.Border
$itemBorder.Style = $revertWindow.FindResource('RevertItemBorderStyle')
$row = New-Object System.Windows.Controls.StackPanel
$row.Style = $revertWindow.FindResource('RevertItemRowStyle')
$checkbox = New-Object System.Windows.Controls.CheckBox
$checkbox.Content = $label
$checkbox.Tag = $setting.Name
$checkbox.Style = $featureCheckboxStyle
$checkbox.IsEnabled = $canUndo
$undoText = New-Object System.Windows.Controls.TextBlock
$undoText.Text = if ($canUndo) { "Revert to: $undoLabel" } else { 'Revert not supported for this setting' }
$undoText.Style = $revertWindow.FindResource('RevertItemUndoTextStyle')
$undoText.Opacity = if ($canUndo) { 0.75 } else { 0.4 }
$row.Children.Add($checkbox) | Out-Null
$row.Children.Add($undoText) | Out-Null
$itemBorder.Child = $row
$itemsPanel.Children.Add($itemBorder) | Out-Null
$entryCheckboxes += $checkbox
}
# Remove the divider from the last entry for cleaner list termination.
if ($itemsPanel.Children.Count -gt 0) {
$lastItem = $itemsPanel.Children[$itemsPanel.Children.Count - 1]
if ($lastItem -is [System.Windows.Controls.Border]) {
$lastItem.BorderThickness = [System.Windows.Thickness]::new(0)
}
}
if ($entryCheckboxes.Count -eq 0) {
$emptyText = New-Object System.Windows.Controls.TextBlock
$emptyText.Text = 'No previously applied tweaks can be reverted'
$emptyText.Style = $revertWindow.FindResource('RevertEmptyTextStyle')
$itemsPanel.Children.Add($emptyText) | Out-Null
$selectAllBtn.IsEnabled = $false
$clearBtn.IsEnabled = $false
}
$updateState = {
$selectedCount = 0
foreach ($cb in $entryCheckboxes) {
if ($cb.IsEnabled -and $cb.IsChecked -eq $true) {
$selectedCount++
}
}
$countText.Text = "$selectedCount settings selected"
$applyBtn.IsEnabled = ($selectedCount -gt 0)
}
foreach ($cb in $entryCheckboxes) {
$cb.Add_Checked($updateState)
$cb.Add_Unchecked($updateState)
}
$selectAllBtn.Add_Click({
foreach ($cb in $entryCheckboxes) {
if ($cb.IsEnabled) {
$cb.IsChecked = $true
}
}
})
$clearBtn.Add_Click({
foreach ($cb in $entryCheckboxes) {
if ($cb.IsEnabled) {
$cb.IsChecked = $false
}
}
})
$cancelHandler = {
$revertWindow.Close()
}
$cancelBtn.Add_Click($cancelHandler)
$applyBtn.Add_Click({
$selected = @()
foreach ($cb in $entryCheckboxes) {
if ($cb.IsEnabled -and $cb.IsChecked -eq $true -and $cb.Tag) {
$selected += $cb.Tag
}
}
$revertWindow.Tag = [PSCustomObject]@{
SelectedFeatureIds = $selected
RestartExplorer = ($restartExplorerCheckbox -and $restartExplorerCheckbox.IsChecked -eq $true)
}
$revertWindow.Close()
})
$revertWindow.ShowDialog() | Out-Null
if ($overlay -and -not $overlayWasAlreadyVisible) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
}
catch { }
}
if ($revertWindow.Tag) {
return $revertWindow.Tag
}
return [PSCustomObject]@{
SelectedFeatureIds = @()
RestartExplorer = $false
}
}

View File

@@ -1,6 +1,7 @@
param ( param (
[switch]$CLI, [switch]$CLI,
[switch]$Silent, [switch]$Silent,
[switch]$Undo,
[switch]$Verbose, [switch]$Verbose,
[switch]$Sysprep, [switch]$Sysprep,
[string]$LogPath, [string]$LogPath,
@@ -96,11 +97,7 @@ param (
[switch]$HideMusic, [switch]$HideMusic,
[switch]$HideIncludeInLibrary, [switch]$HideIncludeInLibrary,
[switch]$HideGiveAccessTo, [switch]$HideGiveAccessTo,
[switch]$HideShare, [switch]$HideShare
[switch]$ShowDriveLettersFirst,
[switch]$ShowDriveLettersLast,
[switch]$ShowNetworkDriveLettersFirst,
[switch]$HideDriveLetters
) )
# Show error if current powershell environment does not have LanguageMode set to FullLanguage # Show error if current powershell environment does not have LanguageMode set to FullLanguage

View File

@@ -0,0 +1,17 @@
# Returns the feature metadata for a parameter when it supports undo; otherwise returns $null.
function GetUndoFeatureForParam {
param (
[string]$paramKey
)
if (-not $script:Features -or -not $script:Features.ContainsKey($paramKey)) {
return $null
}
$feature = $script:Features[$paramKey]
if (-not ($feature.RegistryUndoKey -and ($feature.UndoText -or $feature.UndoAction))) {
return $null
}
return $feature
}

View File

@@ -4,6 +4,7 @@
param ( param (
[switch]$CLI, [switch]$CLI,
[switch]$Silent, [switch]$Silent,
[switch]$Undo,
[switch]$Sysprep, [switch]$Sysprep,
[string]$LogPath, [string]$LogPath,
[string]$User, [string]$User,
@@ -98,17 +99,14 @@ param (
[switch]$HideMusic, [switch]$HideMusic,
[switch]$HideIncludeInLibrary, [switch]$HideIncludeInLibrary,
[switch]$HideGiveAccessTo, [switch]$HideGiveAccessTo,
[switch]$HideShare, [switch]$HideShare
[switch]$ShowDriveLettersFirst,
[switch]$ShowDriveLettersLast,
[switch]$ShowNetworkDriveLettersFirst,
[switch]$HideDriveLetters
) )
# Define script-level variables & paths # Define script-level variables & paths
$script:Version = "2026.03.15" $script:Version = "2026.03.15"
$script:FeaturesConfigVersion = "2.0"
$script:AppsListFilePath = "$PSScriptRoot/Config/Apps.json" $script:AppsListFilePath = "$PSScriptRoot/Config/Apps.json"
$script:DefaultSettingsFilePath = "$PSScriptRoot/Config/DefaultSettings.json" $script:DefaultSettingsFilePath = "$PSScriptRoot/Config/DefaultSettings.json"
$script:FeaturesFilePath = "$PSScriptRoot/Config/Features.json" $script:FeaturesFilePath = "$PSScriptRoot/Config/Features.json"
@@ -122,12 +120,13 @@ $script:MainWindowSchema = "$PSScriptRoot/Schemas/MainWindow.xaml"
$script:MessageBoxSchema = "$PSScriptRoot/Schemas/MessageBoxWindow.xaml" $script:MessageBoxSchema = "$PSScriptRoot/Schemas/MessageBoxWindow.xaml"
$script:AboutWindowSchema = "$PSScriptRoot/Schemas/AboutWindow.xaml" $script:AboutWindowSchema = "$PSScriptRoot/Schemas/AboutWindow.xaml"
$script:ApplyChangesWindowSchema = "$PSScriptRoot/Schemas/ApplyChangesWindow.xaml" $script:ApplyChangesWindowSchema = "$PSScriptRoot/Schemas/ApplyChangesWindow.xaml"
$script:RevertSettingsWindowSchema = "$PSScriptRoot/Schemas/RevertSettingsWindow.xaml"
$script:SharedStylesSchema = "$PSScriptRoot/Schemas/SharedStyles.xaml" $script:SharedStylesSchema = "$PSScriptRoot/Schemas/SharedStyles.xaml"
$script:BubbleHintSchema = "$PSScriptRoot/Schemas/BubbleHint.xaml" $script:BubbleHintSchema = "$PSScriptRoot/Schemas/BubbleHint.xaml"
$script:ImportExportConfigSchema = "$PSScriptRoot/Schemas/ImportExportConfigWindow.xaml" $script:ImportExportConfigSchema = "$PSScriptRoot/Schemas/ImportExportConfigWindow.xaml"
$script:LoadAppsDetailsScriptPath = "$PSScriptRoot/Scripts/FileIO/LoadAppsDetailsFromJson.ps1" $script:LoadAppsDetailsScriptPath = "$PSScriptRoot/Scripts/FileIO/LoadAppsDetailsFromJson.ps1"
$script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Silent', 'Sysprep', 'User', 'NoRestartExplorer', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'Config', 'RunAppsListGenerator', 'CLI', 'AppRemovalTarget' $script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Silent', 'Undo', 'Sysprep', 'User', 'NoRestartExplorer', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'Config', 'RunAppsListGenerator', 'CLI', 'AppRemovalTarget'
# Script-level variables for GUI elements # Script-level variables for GUI elements
$script:GuiWindow = $null $script:GuiWindow = $null
@@ -174,9 +173,23 @@ else {
Start-Transcript -Path $script:DefaultLogPath -Append -IncludeInvocationHeader -Force | Out-Null Start-Transcript -Path $script:DefaultLogPath -Append -IncludeInvocationHeader -Force | Out-Null
} }
# Check if script has all required files # Check if script has all required files/directories.
if (-not ((Test-Path $script:DefaultSettingsFilePath) -and (Test-Path $script:AppsListFilePath) -and (Test-Path $script:RegfilesPath) -and (Test-Path $script:AssetsPath) -and (Test-Path $script:AppSelectionSchema) -and (Test-Path $script:ApplyChangesWindowSchema) -and (Test-Path $script:SharedStylesSchema) -and (Test-Path $script:BubbleHintSchema) -and (Test-Path $script:FeaturesFilePath))) { $optionalPathVariables = @('SavedSettingsFilePath', 'CustomAppsListFilePath', 'DefaultLogPath')
Write-Error "Win11Debloat is unable to find required files, please ensure all script files are present" $requiredPathVariables = @(Get-Variable -Scope Script | Where-Object {
$_.Name -match '(FilePath|Schema|ScriptPath|RegfilesPath|AssetsPath)$' -and ($optionalPathVariables -notcontains $_.Name)
} | Select-Object -ExpandProperty Name)
$missingRequiredPaths = @()
foreach ($variableName in $requiredPathVariables) {
$pathValue = Get-Variable -Name $variableName -Scope Script -ValueOnly
if ([String]::IsNullOrWhiteSpace($pathValue) -or -not (Test-Path $pathValue)) {
$missingRequiredPaths += "$variableName => $pathValue"
}
}
if ($missingRequiredPaths.Count -gt 0) {
Write-Error "Win11Debloat is unable to find required files, please ensure all script files are present. Missing: $($missingRequiredPaths -join '; ')"
Write-Output "" Write-Output ""
Write-Output "Press any key to exit..." Write-Output "Press any key to exit..."
$null = [System.Console]::ReadKey() $null = [System.Console]::ReadKey()
@@ -187,6 +200,15 @@ if (-not ((Test-Path $script:DefaultSettingsFilePath) -and (Test-Path $script:Ap
$script:Features = @{} $script:Features = @{}
try { try {
$featuresData = Get-Content -Path $script:FeaturesFilePath -Raw | ConvertFrom-Json $featuresData = Get-Content -Path $script:FeaturesFilePath -Raw | ConvertFrom-Json
if (-not $featuresData.Version -or $featuresData.Version -ne $script:FeaturesConfigVersion) {
Write-Error "Features.json version mismatch (expected $($script:FeaturesConfigVersion), found $($featuresData.Version))"
Write-Output ""
Write-Output "Press any key to exit..."
$null = [System.Console]::ReadKey()
Exit
}
foreach ($feature in $featuresData.Features) { foreach ($feature in $featuresData.Features) {
$script:Features[$feature.FeatureId] = $feature $script:Features[$feature.FeatureId] = $feature
} }
@@ -272,6 +294,7 @@ if (-not $script:WingetInstalled -and -not $Silent) {
. "$PSScriptRoot/Scripts/GUI/Show-MessageBox.ps1" . "$PSScriptRoot/Scripts/GUI/Show-MessageBox.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-ConfigWindow.ps1" . "$PSScriptRoot/Scripts/GUI/Show-ConfigWindow.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-ApplyModal.ps1" . "$PSScriptRoot/Scripts/GUI/Show-ApplyModal.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-RevertSettingsModal.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-AppSelectionWindow.ps1" . "$PSScriptRoot/Scripts/GUI/Show-AppSelectionWindow.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-MainWindow.ps1" . "$PSScriptRoot/Scripts/GUI/Show-MainWindow.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-AboutDialog.ps1" . "$PSScriptRoot/Scripts/GUI/Show-AboutDialog.ps1"
@@ -285,6 +308,7 @@ if (-not $script:WingetInstalled -and -not $Silent) {
. "$PSScriptRoot/Scripts/Helpers/GetFriendlyTargetUserName.ps1" . "$PSScriptRoot/Scripts/Helpers/GetFriendlyTargetUserName.ps1"
. "$PSScriptRoot/Scripts/Helpers/ImportConfigToParams.ps1" . "$PSScriptRoot/Scripts/Helpers/ImportConfigToParams.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetTargetUserForAppRemoval.ps1" . "$PSScriptRoot/Scripts/Helpers/GetTargetUserForAppRemoval.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetUndoFeatureForParam.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetUserDirectory.ps1" . "$PSScriptRoot/Scripts/Helpers/GetUserDirectory.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetUserName.ps1" . "$PSScriptRoot/Scripts/Helpers/GetUserName.ps1"
. "$PSScriptRoot/Scripts/Helpers/TestIfUserIsLoggedIn.ps1" . "$PSScriptRoot/Scripts/Helpers/TestIfUserIsLoggedIn.ps1"
@@ -325,6 +349,27 @@ foreach ($Param in $script:ControlParams) {
} }
} }
# Guard: Undo mode requires at least one actionable and cannot be combined with deployment-targeted parameters
if ($script:Params.ContainsKey('Undo')) {
$deploymentTargetParams = @('Sysprep', 'User', 'AppRemovalTarget')
$selectedDeploymentParams = @($deploymentTargetParams | Where-Object { $script:Params.ContainsKey($_) })
if ($selectedDeploymentParams.Count -gt 0) {
Write-Error "The -Undo parameter cannot be combined with deployment target parameters: -$($selectedDeploymentParams -join ', -')."
AwaitKeyToExit
}
$loadsSettingsFromPreset = $RunDefaults -or $RunDefaultsLite -or $RunSavedSettings
$undoTargets = @($script:Params.Keys | Where-Object {
($script:ControlParams -notcontains $_) -and $_ -ne 'Apps' -and $_ -ne 'CreateRestorePoint'
})
if ($undoTargets.Count -eq 0 -and -not $loadsSettingsFromPreset) {
Write-Error "The -Undo parameter requires at least one setting/feature parameter to revert."
AwaitKeyToExit
}
}
# Hide progress bars for app removal, as they block Win11Debloat's output # Hide progress bars for app removal, as they block Win11Debloat's output
if (-not ($script:Params.ContainsKey("Verbose"))) { if (-not ($script:Params.ContainsKey("Verbose"))) {
$ProgressPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue'
@@ -423,7 +468,7 @@ if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSa
Exit Exit
} }
catch { catch {
Write-Warning "Unable to load WPF GUI (not supported in this environment), falling back to CLI mode" Write-Warning "Something went wrong while loading the graphical interface, falling back to CLI mode: $_"
if (-not $Silent) { if (-not $Silent) {
Write-Host "" Write-Host ""
Write-Host "Press any key to continue..." Write-Host "Press any key to continue..."
@@ -464,9 +509,17 @@ if (($controlParamsCount -eq $script:Params.Keys.Count) -or ($script:Params.Keys
AwaitKeyToExit AwaitKeyToExit
} }
try {
# Execute all selected/provided parameters using the consolidated function # Execute all selected/provided parameters using the consolidated function
# (This also handles restore point creation if requested) # (This also handles restore point creation if requested)
ExecuteAllChanges ExecuteAllChanges
}
catch {
Write-Error "An error occurred while applying changes: $_"
AwaitKeyToExit
}
RestartExplorer RestartExplorer