Merge branch 'master' into undo-tweaks

This commit is contained in:
Raphire
2026-04-26 17:38:53 +02:00
24 changed files with 1306 additions and 355 deletions

View File

@@ -841,6 +841,20 @@
"SelectedByDefault": false,
"Recommendation": "optional"
},
{
"FriendlyName": "Windows Web Experience Pack",
"AppId": "MicrosoftWindows.Client.WebExperience",
"Description": "Helps deliver and update certain features, like Widgets, through the Microsoft Store",
"SelectedByDefault": false,
"Recommendation": "optional"
},
{
"FriendlyName": "Widgets Platform Runtime",
"AppId": "Microsoft.WidgetsPlatformRuntime",
"Description": "Runtime required for Windows Widgets to function",
"SelectedByDefault": false,
"Recommendation": "optional"
},
{
"FriendlyName": "HP AI Experience Center",
"AppId": "AD2F1837.HPAIExperienceCenter",

View File

@@ -29,10 +29,6 @@
"Name": "DisableStoreSearchSuggestions",
"Value": true
},
{
"Name": "DisableSearchHighlights",
"Value": true
},
{
"Name": "DisableCopilot",
"Value": true
@@ -65,10 +61,6 @@
"Name": "DisableDragTray",
"Value": true
},
{
"Name": "DisableFastStartup",
"Value": true
},
{
"Name": "Hide3dObjects",
"Value": true

View File

@@ -187,6 +187,7 @@
"Label": "Open File Explorer to",
"ToolTip": "This setting allows you to choose the default location that File Explorer opens to.",
"Category": "File Explorer",
"Priority": 1,
"Values": [
{
"Label": "Home",
@@ -214,6 +215,39 @@
}
]
},
{
"GroupId": "DriveLetterPosition",
"Label": "Drive letter position",
"ToolTip": "This setting allows you to choose where drive letters are shown in File Explorer.",
"Category": "File Explorer",
"Priority": 20,
"Values": [
{
"Label": "Show drive letters after drive label (Default)",
"FeatureIds": [
"ShowDriveLettersLast"
]
},
{
"Label": "Show drive letters before drive label",
"FeatureIds": [
"ShowDriveLettersFirst"
]
},
{
"Label": "Show network drive letters before drive label",
"FeatureIds": [
"ShowNetworkDriveLettersFirst"
]
},
{
"Label": "Hide all drive letters",
"FeatureIds": [
"HideDriveLetters"
]
}
]
},
{
"GroupId": "ShowTabsInAltTab",
"Label": "Show tabs from apps when snapping or pressing Alt+Tab",
@@ -1007,7 +1041,7 @@
},
{
"FeatureId": "DisableBitlockerAutoEncryption",
"ToolTip": "For devices that support it, Windows 11 automatically enables BitLocker device encryption. Disabling this will turn off automatic encryption of the device, but you can still manually enable BitLocker encryption if desired.",
"ToolTip": "For devices that support it, Windows 11 automatically enables BitLocker device encryption. Disabling this will turn off automatic encryption of the device, but you can still manually enable BitLocker encryption if desired. Drives that are already encrypted with BitLocker will remain encrypted when this setting is applied.",
"Category": "System",
"Action": "Disable BitLocker automatic device encryption",
"ApplyText": "Disabling BitLocker automatic device encryption...",
@@ -1219,6 +1253,7 @@
"FeatureId": "ShowKnownFileExt",
"ToolTip": "This will show file extensions for known file types. By default, Windows hides file extensions for known file types which can lead to confusion and security risks.",
"Category": "File Explorer",
"Priority": 2,
"Action": "Show file extensions for known file types",
"ApplyText": "Showing file extensions for known file types...",
"UndoAction": "Hide file extensions for known file types",
@@ -1232,6 +1267,7 @@
"FeatureId": "ShowHiddenFolders",
"ToolTip": "By default, Windows hides certain files, folders and drives to prevent accidental modification or deletion. Turn this on to show all files in File Explorer.",
"Category": "File Explorer",
"Priority": 3,
"Action": "Show hidden files, folders and drives",
"ApplyText": "Showing hidden files, folders and drives...",
"UndoAction": "Hide hidden files, folders and drives",
@@ -1241,10 +1277,25 @@
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "HideDupliDrive",
"ToolTip": "By default, Windows shows removable drives both under 'This PC' and in the navigation pane with its own entry. Enable this setting to only show removable drives under 'This PC'.",
"Category": "File Explorer",
"Priority": 4,
"Action": "Hide duplicate removable drive entries",
"ApplyText": "Hiding duplicate removable drive entries from the File Explorer navigation pane...",
"UndoAction": "Show duplicate removable drive entries",
"UndoText": "Showing duplicate removable drive entries...",
"RegistryKey": "Hide_duplicate_removable_drives_from_navigation_pane_of_File_Explorer.reg",
"RegistryUndoKey": "Show_duplicate_removable_drives_in_navigation_pane_of_File_Explorer.reg",
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "HideHome",
"ToolTip": "Hides the 'Home' section from the File Explorer navigation pane.",
"Category": "File Explorer",
"Priority": 5,
"Action": "Hide 'Home' from navigation pane",
"ApplyText": "Hiding 'Home' from the File Explorer navigation pane...",
"UndoAction": "Show 'Home' from navigation pane",
@@ -1258,6 +1309,7 @@
"FeatureId": "HideGallery",
"ToolTip": "Hides the 'Gallery' section from the File Explorer navigation pane.",
"Category": "File Explorer",
"Priority": 6,
"Action": "Hide 'Gallery' from navigation pane",
"ApplyText": "Hiding 'Gallery' from the File Explorer navigation pane...",
"UndoAction": "Show 'Gallery' from navigation pane",
@@ -1267,32 +1319,6 @@
"MinVersion": 22000,
"MaxVersion": null
},
{
"FeatureId": "HideDupliDrive",
"ToolTip": "By default, Windows shows removable drives both under 'This PC' and in the navigation pane with its own entry. Enable this setting to only show removable drives under 'This PC'.",
"Category": "File Explorer",
"Action": "Hide duplicate removable drive entries",
"ApplyText": "Hiding duplicate removable drive entries from the File Explorer navigation pane...",
"UndoAction": "Show duplicate removable drive entries",
"UndoText": "Showing duplicate removable drive entries...",
"RegistryKey": "Hide_duplicate_removable_drives_from_navigation_pane_of_File_Explorer.reg",
"RegistryUndoKey": "Show_duplicate_removable_drives_in_navigation_pane_of_File_Explorer.reg",
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "AddFoldersToThisPC",
"ToolTip": "This setting will add common folders like Desktop, Documents, Downloads, Music, Pictures and Videos back to the 'This PC' page in File Explorer.",
"Category": "File Explorer",
"Action": "Add common folders back to 'This PC' page",
"ApplyText": "Adding all common folders (Desktop, Downloads, etc.) back to 'This PC' in File Explorer...",
"UndoAction": "Remove common folders from 'This PC' page",
"UndoText": "Removing common folders from 'This PC' page...",
"RegistryKey": "Add_All_Folders_Under_This_PC.reg",
"RegistryUndoKey": "Remove_All_Folders_Under_This_PC.reg",
"MinVersion": 22000,
"MaxVersion": null
},
{
"FeatureId": "DisableTransparency",
"ToolTip": "This will disable transparency effects on Windows and interfaces. Which can help improve performance on older hardware.",
@@ -1371,49 +1397,11 @@
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "HideIncludeInLibrary",
"ToolTip": "Hides the 'Include in library' option from the File Explorer context menu.",
"Category": "File Explorer",
"Action": "Hide 'Include in library' option in the context menu",
"ApplyText": "Hiding 'Include in library' in the context menu...",
"UndoAction": "Show 'Include in library' option in the context menu",
"UndoText": "Showing 'Include in library' option in the context menu...",
"RegistryKey": "Disable_Include_in_library_from_context_menu.reg",
"RegistryUndoKey": "Enable_Include_in_library_in_context_menu.reg",
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "HideGiveAccessTo",
"ToolTip": "Hides the 'Give access to' option from the File Explorer context menu.",
"Category": "File Explorer",
"Action": "Hide 'Give access to' option in the context menu",
"ApplyText": "Hiding 'Give access to' in the context menu...",
"UndoAction": "Show 'Give access to' option in the context menu",
"UndoText": "Showing 'Give access to' option in the context menu...",
"RegistryKey": "Disable_Give_access_to_context_menu.reg",
"RegistryUndoKey": "Enable_Give_access_to_context_menu.reg",
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "HideShare",
"ToolTip": "Hides the 'Share' option from the File Explorer context menu.",
"Category": "File Explorer",
"Action": "Hide 'Share' option in the context menu",
"ApplyText": "Hiding 'Share' in the context menu...",
"UndoAction": "Show 'Share' option in the context menu",
"UndoText": "Showing 'Share' option in the context menu...",
"RegistryKey": "Disable_Share_from_context_menu.reg",
"RegistryUndoKey": "Enable_Share_in_context_menu.reg",
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "HideOnedrive",
"ToolTip": "Hides the 'OneDrive' folder from the File Explorer navigation pane.",
"Category": "File Explorer",
"Priority": 7,
"Action": "Hide 'OneDrive' folder from navigation pane",
"ApplyText": "Hiding the 'OneDrive' folder from the File Explorer navigation pane...",
"UndoAction": "Show 'OneDrive' folder from navigation pane",
@@ -1421,12 +1409,13 @@
"RegistryKey": "Hide_Onedrive_Folder.reg",
"RegistryUndoKey": "Show_Onedrive_Folder.reg",
"MinVersion": null,
"MaxVersion": 21999
"MaxVersion": null
},
{
"FeatureId": "Hide3dObjects",
"ToolTip": "Hides the '3D objects' folder from the File Explorer navigation pane.",
"Category": "File Explorer",
"Priority": 8,
"Action": "Hide '3D objects' folder from navigation pane",
"ApplyText": "Hiding the '3D objects' folder from the File Explorer navigation pane...",
"UndoAction": "Show '3D objects' folder in the File Explorer navigation pane",
@@ -1440,6 +1429,7 @@
"FeatureId": "HideMusic",
"ToolTip": "Hides the 'Music' folder from the File Explorer navigation pane.",
"Category": "File Explorer",
"Priority": 9,
"Action": "Hide 'Music' folder from navigation pane",
"ApplyText": "Hiding the 'Music' folder from the File Explorer navigation pane...",
"UndoAction": "Show 'Music' folder in the File Explorer navigation pane",
@@ -1449,6 +1439,62 @@
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "AddFoldersToThisPC",
"ToolTip": "This setting will add common folders like Desktop, Documents, Downloads, Music, Pictures and Videos back to the 'This PC' page in File Explorer.",
"Category": "File Explorer",
"Priority": 10,
"Action": "Add common folders back to 'This PC' page",
"ApplyText": "Adding all common folders (Desktop, Downloads, etc.) back to 'This PC' in File Explorer...",
"UndoAction": "Remove common folders from 'This PC' page",
"UndoText": "Removing common folders from 'This PC' page...",
"RegistryKey": "Add_All_Folders_Under_This_PC.reg",
"RegistryUndoKey": "Remove_All_Folders_Under_This_PC.reg",
"MinVersion": 22000,
"MaxVersion": null
},
{
"FeatureId": "HideIncludeInLibrary",
"ToolTip": "Hides the 'Include in library' option from the File Explorer context menu.",
"Category": "File Explorer",
"Priority": 11,
"Action": "Hide 'Include in library' option in the context menu",
"ApplyText": "Hiding 'Include in library' in the context menu...",
"UndoAction": "Show 'Include in library' option in the context menu",
"UndoText": "Showing 'Include in library' option in the context menu...",
"RegistryKey": "Disable_Include_in_library_from_context_menu.reg",
"RegistryUndoKey": "Enable_Include_in_library_in_context_menu.reg",
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "HideGiveAccessTo",
"ToolTip": "Hides the 'Give access to' option from the File Explorer context menu.",
"Category": "File Explorer",
"Priority": 12,
"Action": "Hide 'Give access to' option in the context menu",
"ApplyText": "Hiding 'Give access to' in the context menu...",
"UndoAction": "Show 'Give access to' option in the context menu",
"UndoText": "Showing 'Give access to' option in the context menu...",
"RegistryKey": "Disable_Give_access_to_context_menu.reg",
"RegistryUndoKey": "Enable_Give_access_to_context_menu.reg",
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "HideShare",
"ToolTip": "Hides the 'Share' option from the File Explorer context menu.",
"Category": "File Explorer",
"Priority": 13,
"Action": "Hide 'Share' option in the context menu",
"ApplyText": "Hiding 'Share' in the context menu...",
"UndoAction": "Show 'Share' option in the context menu",
"UndoText": "Showing 'Share' option in the context menu...",
"RegistryKey": "Disable_Share_from_context_menu.reg",
"RegistryUndoKey": "Enable_Share_in_context_menu.reg",
"MinVersion": null,
"MaxVersion": 21999
},
{
"FeatureId": "DisableBraveBloat",
"ToolTip": "This will disable Brave's built-in AI features, Crypto wallet, News, Rewards, Talk and VPN. This feature uses policies, which will lock down certain settings.",
@@ -1489,6 +1535,58 @@
"RequiresReboot": true,
"MinVersion": 22000,
"MaxVersion": null
},
{
"FeatureId": "ShowDriveLettersFirst",
"ToolTip": "This setting will show drive letters before the drive label in File Explorer.",
"Category": "File Explorer",
"Action": "Show all drive letters before drive label",
"ApplyText": "Showing drive letters before drive label...",
"UndoAction": "Show all drive letters after drive label",
"UndoText": "Showing drive letters after drive label...",
"RegistryKey": "Show_Drive_Letters_First.reg",
"RegistryUndoKey": "Undo/Show_Drive_Letters_Last.reg",
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "ShowDriveLettersLast",
"ToolTip": "This setting will show drive letters after the drive label in File Explorer (Default Windows behavior).",
"Category": "File Explorer",
"Action": "Show all drive letters after drive label",
"ApplyText": "Showing drive letters after drive label...",
"UndoAction": null,
"UndoText": null,
"RegistryKey": "Show_Drive_Letters_Last.reg",
"RegistryUndoKey": null,
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "ShowNetworkDriveLettersFirst",
"ToolTip": "This setting will show only network drive letters before the drive label in File Explorer.",
"Category": "File Explorer",
"Action": "Show network drive letters before drive label",
"ApplyText": "Showing network drive letters before drive label...",
"UndoAction": "Show all drive letters after drive label",
"UndoText": "Showing drive letters after drive label...",
"RegistryKey": "Show_Network_Drive_Letters_First.reg",
"RegistryUndoKey": "Undo/Show_Drive_Letters_Last.reg",
"MinVersion": null,
"MaxVersion": null
},
{
"FeatureId": "HideDriveLetters",
"ToolTip": "This setting will hide all drive letters from the File Explorer navigation pane and 'This PC'.",
"Category": "File Explorer",
"Action": "Hide all drive letters",
"ApplyText": "Hiding all drive letters...",
"UndoAction": "Show all drive letters after drive label",
"UndoText": "Showing drive letters after drive label...",
"RegistryKey": "Hide_Drive_Letters.reg",
"RegistryUndoKey": "Undo/Show_Drive_Letters_Last.reg",
"MinVersion": null,
"MaxVersion": null
}
]
}

View File

@@ -160,6 +160,7 @@ 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.
- 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.
- Change drive letter position or visibility in File Explorer.
#### Multi-tasking

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -43,7 +43,8 @@
</Grid>
<!-- Content -->
<StackPanel Grid.Row="1" x:Name="ContentPanel" Margin="20,12,20,9">
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Padding="0,0,8,0">
<StackPanel x:Name="ContentPanel" Margin="20,12,20,9">
<TextBlock x:Name="PromptText"
TextWrapping="Wrap"
FontSize="14"
@@ -53,6 +54,7 @@
<!-- Checkboxes are added dynamically at runtime -->
<StackPanel x:Name="CheckboxPanel"/>
</StackPanel>
</ScrollViewer>
<!-- Button Footer -->
<Border Grid.Row="2"

View File

@@ -1,15 +1,22 @@
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
Title="Win11Debloat"
MinWidth="1130" MinHeight="600"
MaxWidth="1400"
ResizeMode="CanResize"
SnapsToDevicePixels="True"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
AllowsTransparency="False"
Background="{DynamicResource BgColor}"
Foreground="{DynamicResource FgColor}">
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome ResizeBorderThickness="5"
CaptionHeight="32"
CornerRadius="8"
GlassFrameThickness="0"
UseAeroCaptionButtons="False"/>
</shell:WindowChrome.WindowChrome>
<Window.Resources>
<!-- Sort column header hover style -->
<Style x:Key="SortHeaderBtnStyle" TargetType="StackPanel">
@@ -513,38 +520,30 @@
</Style>
</Window.Resources>
<Border BorderBrush="{DynamicResource BorderColor}"
<Border x:Name="MainBorder" BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
CornerRadius="8"
Background="{DynamicResource BgColor}"
Margin="25">
<Border.Effect>
<DropShadowEffect Color="Black"
Opacity="0.15"
BlurRadius="20"
ShadowDepth="0"
Direction="0"/>
</Border.Effect>
Margin="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Resize Borders -->
<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"/>
<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"/>
<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"/>
<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"/>
<Rectangle x:Name="ResizeLeft" Width="5" HorizontalAlignment="Left" Fill="Transparent" Cursor="SizeWE" Grid.Row="0" Grid.RowSpan="2" Panel.ZIndex="100" Visibility="Collapsed" IsHitTestVisible="False"/>
<Rectangle x:Name="ResizeRight" Width="5" HorizontalAlignment="Right" Fill="Transparent" Cursor="SizeWE" Grid.Row="0" Grid.RowSpan="2" 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" Visibility="Collapsed" IsHitTestVisible="False"/>
<Rectangle x:Name="ResizeBottom" Height="5" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNS" Grid.Row="1" Panel.ZIndex="100" 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" 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" Visibility="Collapsed" IsHitTestVisible="False"/>
<Rectangle x:Name="ResizeBottomLeft" Width="8" Height="8" HorizontalAlignment="Left" VerticalAlignment="Bottom" Fill="Transparent" Cursor="SizeNESW" Grid.Row="1" 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="1" Panel.ZIndex="101" Visibility="Collapsed" IsHitTestVisible="False"/>
<!-- Custom Title Bar -->
<Grid Grid.Row="0" x:Name="TitleBar">
<Border Background="{DynamicResource BgColor}" CornerRadius="8,8,0,0">
<Border x:Name="TitleBarBackground" Background="{DynamicResource BgColor}" CornerRadius="8,8,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -557,8 +556,8 @@
Margin="12,0,0,0"
FontSize="12"/>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<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" Content="&#xE700;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Options" AutomationProperties.Name="Options">
<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="MenuBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="&#xE700;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Options" AutomationProperties.Name="Options">
<Button.ContextMenu>
<ContextMenu x:Name="MainMenu">
<ContextMenu.Resources>
@@ -607,14 +606,20 @@
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button x:Name="CloseBtn" Content="&#xE8BB;" Style="{StaticResource CloseButton}" ToolTip="Close" AutomationProperties.Name="Close"/>
<Button x:Name="CloseBtn" shell:WindowChrome.IsHitTestVisibleInChrome="True" Content="&#xE8BB;" Style="{StaticResource CloseButton}" ToolTip="Close" AutomationProperties.Name="Close"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- Main Content with Tabs -->
<TabControl Grid.Row="1" Background="Transparent" BorderThickness="0" Margin="0" Padding="0" Name="MainTabControl">
<Grid Grid.Row="1" x:Name="ContentGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl Grid.Row="0" Background="Transparent" BorderThickness="0" Margin="0" Padding="0" Name="MainTabControl">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
@@ -748,6 +753,7 @@
</Style>
</ToggleButton.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE735;" FontFamily="Segoe Fluent Icons" FontSize="14" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="Quick Select" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock x:Name="PresetsArrow" Text="&#xE70D;" FontFamily="Segoe Fluent Icons" FontSize="10" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
@@ -756,15 +762,20 @@
</TextBlock>
</StackPanel>
</ToggleButton>
<Button x:Name="ClearAppSelectionBtn" Content="Clear Selection" ToolTip="Clear all selected apps" Style="{DynamicResource SecondaryButtonStyle}" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="Clear Selection"/>
<Button x:Name="ClearAppSelectionBtn" ToolTip="Clear all selected apps" Style="{DynamicResource SecondaryButtonStyle}" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="Clear Selection">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="&#xE711;" FontFamily="Segoe Fluent Icons" FontSize="14" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="Clear Selection" FontSize="13" VerticalAlignment="Center" Margin="0,0,0,1"/>
</StackPanel>
</Button>
<Popup x:Name="PresetsPopup" PlacementTarget="{Binding ElementName=PresetsBtn}" Placement="Bottom" StaysOpen="True" AllowsTransparency="True" VerticalOffset="2">
<Border Background="{DynamicResource CardBgColor}" BorderBrush="{DynamicResource BorderColor}" BorderThickness="1" CornerRadius="6" Padding="4,6" Margin="12">
<Border.Effect>
<DropShadowEffect BlurRadius="12" Opacity="0.25" ShadowDepth="4"/>
</Border.Effect>
<StackPanel x:Name="PresetsPanel" MinWidth="220">
<CheckBox x:Name="PresetDefaultApps" Content="Default selection" IsThreeState="True" Foreground="{DynamicResource FgColor}" Margin="8,4" AutomationProperties.Name="Default selection"/>
<CheckBox x:Name="PresetLastUsed" Content="Last used selection" IsThreeState="True" Foreground="{DynamicResource FgColor}" Margin="8,4" AutomationProperties.Name="Last used selection"/>
<CheckBox x:Name="PresetDefaultApps" Content="Default apps" IsThreeState="True" ToolTip="Select the apps that are safe to remove for most users" Foreground="{DynamicResource FgColor}" Margin="8,4" AutomationProperties.Name="Default selection"/>
<CheckBox x:Name="PresetLastUsed" Content="Last used apps" IsThreeState="True" ToolTip="Select the apps that were removed the last time Win11Debloat was run" Foreground="{DynamicResource FgColor}" Margin="8,4" AutomationProperties.Name="Last used selection"/>
<Separator Margin="4,6" Background="{DynamicResource BorderColor}"/>
<StackPanel x:Name="JsonPresetsPanel"/>
</StackPanel>
@@ -895,9 +906,67 @@
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<Button x:Name="LoadDefaultsBtn" Content="Select Default Settings" ToolTip="Select the settings that are recommended for most people" Style="{DynamicResource SecondaryButtonStyle}" Padding="10,0" Height="32" Margin="0,0,10,0" AutomationProperties.Name="Select Default Settings"/>
<Button x:Name="LoadLastUsedBtn" Content="Select Last Used Settings" ToolTip="Select the settings that were used the last time Win11Debloat was run" Style="{DynamicResource SecondaryButtonStyle}" Padding="10,0" Height="32" Margin="0,0,10,0" AutomationProperties.Name="Select Last Used Settings"/>
<Button x:Name="ClearAllTweaksBtn" Content="Clear Selection" ToolTip="Clear all selected tweaks" Style="{DynamicResource SecondaryButtonStyle}" Padding="10,0" Height="32" Margin="0,0,10,0" AutomationProperties.Name="Clear Selection"/>
<ToggleButton x:Name="TweaksPresetsBtn" ToolTip="Select tweak presets" Height="32" Padding="10,0" Margin="0,0,10,0" AutomationProperties.Name="Tweak Presets">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Background" Value="{DynamicResource SecondaryButtonBg}"/>
<Setter Property="Foreground" Value="{DynamicResource FgColor}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderColor}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,1"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource SecondaryButtonHover}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource SecondaryButtonPressed}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="{DynamicResource SecondaryButtonHover}"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE735;" FontFamily="Segoe Fluent Icons" FontSize="14" VerticalAlignment="Center" Margin="0,1,6,0"/>
<TextBlock Text="Quick Select" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock x:Name="TweaksPresetsArrow" Text="&#xE70D;" FontFamily="Segoe Fluent Icons" FontSize="10" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<RotateTransform Angle="0"/>
</TextBlock.RenderTransform>
</TextBlock>
</StackPanel>
</ToggleButton>
<Button x:Name="ClearAllTweaksBtn" ToolTip="Clear all selected tweaks" Style="{DynamicResource SecondaryButtonStyle}" Padding="10,0" Height="32" Margin="0,0,10,0" AutomationProperties.Name="Clear Selection">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="&#xE711;" FontFamily="Segoe Fluent Icons" FontSize="14" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="Clear Selection" FontSize="13" VerticalAlignment="Center" Margin="0,0,0,1"/>
</StackPanel>
</Button>
<Popup x:Name="TweaksPresetsPopup" PlacementTarget="{Binding ElementName=TweaksPresetsBtn}" Placement="Bottom" StaysOpen="True" AllowsTransparency="True" VerticalOffset="2">
<Border Background="{DynamicResource CardBgColor}" BorderBrush="{DynamicResource BorderColor}" BorderThickness="1" CornerRadius="6" Padding="4,6" Margin="12">
<Border.Effect>
<DropShadowEffect BlurRadius="12" Opacity="0.25" ShadowDepth="4"/>
</Border.Effect>
<StackPanel MinWidth="220">
<CheckBox x:Name="PresetDefaultTweaksBtn" Content="Default settings" IsThreeState="True" ToolTip="Select the settings that are recommended for most people" Foreground="{DynamicResource FgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="Default settings"/>
<CheckBox x:Name="PresetLastUsedTweaksBtn" Content="Last used settings" IsThreeState="True" ToolTip="Select the settings that were used the last time Win11Debloat was run" Foreground="{DynamicResource FgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="Last used settings"/>
<Separator Margin="4,6" Background="{DynamicResource BorderColor}"/>
<CheckBox x:Name="PresetPrivacyTweaksBtn" Content="Privacy &amp; Suggested Content" IsThreeState="True" ToolTip="Select all Privacy &amp; Suggested Content tweaks" Foreground="{DynamicResource FgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="All Privacy and Suggested Content"/>
<CheckBox x:Name="PresetAITweaksBtn" Content="AI features" IsThreeState="True" ToolTip="Select all AI feature tweaks" Foreground="{DynamicResource FgColor}" Style="{DynamicResource PresetCheckBoxStyle}" AutomationProperties.Name="All AI features"/>
</StackPanel>
</Border>
</Popup>
</StackPanel>
<Border x:Name="TweakSearchBorder" Grid.Column="2">
@@ -1096,7 +1165,7 @@
</TabControl>
<!-- Bottom Navigation Buttons -->
<Grid Grid.Row="2" Height="56" x:Name="BottomNavGrid">
<Grid Grid.Row="1" Height="56" x:Name="BottomNavGrid">
<Grid Margin="20,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -1127,10 +1196,11 @@
<Ellipse x:Name="ProgressIndicator3" Width="10" Height="10" Margin="4,0" Fill="#808080" ToolTip="Deployment Settings"/>
</StackPanel>
</Grid>
</Grid>
<!-- Modal Overlay for MessageBoxes -->
<Rectangle x:Name="ModalOverlay"
Grid.RowSpan="3"
Grid.RowSpan="2"
Fill="#80000000"
Visibility="Collapsed"
Panel.ZIndex="1000"

View File

@@ -49,8 +49,8 @@ function ExecuteParameter {
RemoveApps 'Microsoft.Copilot'
}
'DisableWidgets' {
# Also remove the app package for Widgets
RemoveApps 'Microsoft.StartExperiencesApp'
# Also remove the app packages for Widgets
RemoveApps 'Microsoft.StartExperiencesApp','MicrosoftWindows.Client.WebExperience','Microsoft.WidgetsPlatformRuntime'
}
}
return

View File

@@ -5,7 +5,8 @@ function Show-ImportExportConfigWindow {
[string]$Title,
[string]$Prompt,
[string[]]$Categories = @('Applications', 'System Tweaks', 'Deployment Settings'),
[string[]]$DisabledCategories = @()
[string[]]$DisabledCategories = @(),
[hashtable]$CategoryDetails = @()
)
# Show overlay on owner window
@@ -56,11 +57,18 @@ function Show-ImportExportConfigWindow {
$checkboxPanel = $dlg.FindName('CheckboxPanel')
$checkboxes = @{}
foreach ($cat in $Categories) {
# Create a container for the checkbox and details
$container = New-Object System.Windows.Controls.StackPanel
$container.Orientation = [System.Windows.Controls.Orientation]::Vertical
$container.Margin = [System.Windows.Thickness]::new(0,0,0,12)
# Create checkbox
$cb = New-Object System.Windows.Controls.CheckBox
$cb.Content = $cat
$cb.IsChecked = $true
$cb.Margin = [System.Windows.Thickness]::new(0,0,0,8)
$cb.Margin = [System.Windows.Thickness]::new(0,0,0,4)
$cb.FontSize = 14
$cb.FontWeight = [System.Windows.FontWeights]::Medium
$cb.Foreground = $dlg.FindResource('FgColor')
$cb.Style = $window.Resources["AppsPanelCheckBoxStyle"]
if ($DisabledCategories -contains $cat) {
@@ -69,7 +77,22 @@ function Show-ImportExportConfigWindow {
$cb.Opacity = 0.65
$cb.ToolTip = 'No selected settings available in this category.'
}
$checkboxPanel.Children.Add($cb) | Out-Null
$container.Children.Add($cb) | Out-Null
# Add details if available
if ($CategoryDetails -and $CategoryDetails[$cat]) {
$detailsText = New-Object System.Windows.Controls.TextBlock
$detailsText.Text = $CategoryDetails[$cat]
$detailsText.FontSize = 12
$detailsText.Foreground = $dlg.FindResource('FgColor')
$detailsText.Margin = [System.Windows.Thickness]::new(30,0,0,0)
$detailsText.Opacity = 0.75
$detailsText.TextWrapping = [System.Windows.TextWrapping]::Wrap
$container.Children.Add($detailsText) | Out-Null
}
$checkboxPanel.Children.Add($container) | Out-Null
$checkboxes[$cat] = $cb
}
@@ -202,6 +225,72 @@ function Get-AvailableImportExportCategories {
return $availableCategories
}
function Get-DeploymentCategoryDetailString {
param (
[array]$DeploymentSettings
)
$lookup = @{}
foreach ($setting in @($DeploymentSettings)) {
if ($setting -and $setting.Name) {
$lookup[$setting.Name] = $setting.Value
}
}
$line1 = @()
if ($lookup.ContainsKey('UserSelectionIndex')) {
switch ([int]$lookup['UserSelectionIndex']) {
0 { $line1 += 'User: Current User' }
1 { $line1 += "User: $(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { 'Other User' })" }
2 { $line1 += 'User: Sysprep' }
}
}
if ($lookup.ContainsKey('AppRemovalScopeIndex')) {
switch ([int]$lookup['AppRemovalScopeIndex']) {
0 { $line1 += 'App Removal: All Users' }
1 { $line1 += 'App Removal: Current User' }
2 { $line1 += "App Removal: $(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { 'Other User' })" }
}
}
$options = @()
if ($lookup.ContainsKey('CreateRestorePoint') -and [bool]$lookup['CreateRestorePoint']) { $options += 'Restore Point' }
if ($lookup.ContainsKey('RestartExplorer') -and [bool]$lookup['RestartExplorer']) { $options += 'Restart Explorer' }
$lines = @()
if ($line1.Count -gt 0) { $lines += $line1 -join ', ' }
if ($options.Count -gt 0) { $lines += "Options: $($options -join ', ')" }
if ($lines.Count -gt 0) { return $lines -join "`n" }
return 'Default deployment settings'
}
function Build-CategoryDetails {
param (
[int]$AppCount = 0,
[int]$TweakCount = 0,
[array]$DeploymentSettings
)
$details = @{}
if ($AppCount -gt 0) {
$details['Applications'] = "$AppCount app$(if ($AppCount -ne 1) { 's' })"
}
if ($TweakCount -gt 0) {
$details['System Tweaks'] = "$TweakCount tweak$(if ($TweakCount -ne 1) { 's' })"
}
if ($DeploymentSettings) {
$details['Deployment Settings'] = Get-DeploymentCategoryDetailString -DeploymentSettings $DeploymentSettings
}
return $details
}
function Apply-ImportedApplications {
param (
[System.Windows.Controls.Panel]$AppsPanel,
@@ -280,7 +369,10 @@ function Export-Configuration {
if ($selectedApps.Count -eq 0) { $disabledCategories += 'Applications' }
if ($tweakSettings.Count -eq 0) { $disabledCategories += 'System Tweaks' }
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Export Configuration' -Prompt 'Select which settings to include in the export:' -DisabledCategories $disabledCategories
$deploymentSettings = Get-DeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox
$categoryDetails = Build-CategoryDetails -AppCount $selectedApps.Count -TweakCount $tweakSettings.Count -DeploymentSettings $deploymentSettings
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Export Configuration' -Prompt 'Select which settings to include in the export:' -DisabledCategories $disabledCategories -CategoryDetails $categoryDetails
if (-not $categories) { return }
$config = @{ Version = '1.0' }
@@ -292,7 +384,7 @@ function Export-Configuration {
$config['Tweaks'] = @($tweakSettings)
}
if ($categories -contains 'Deployment Settings') {
$config['Deployment'] = @(Get-DeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox)
$config['Deployment'] = @($deploymentSettings)
}
# Show native save-file dialog
@@ -350,7 +442,11 @@ function Import-Configuration {
return
}
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Import Configuration' -Prompt 'Select which settings to import:' -Categories $availableCategories
$appCount = @($config.Apps | Where-Object { $_ -is [string] -and -not [string]::IsNullOrWhiteSpace($_) }).Count
$tweakCount = @($config.Tweaks | Where-Object { $_ -and $_.Name -and $_.Value -eq $true }).Count
$categoryDetails = Build-CategoryDetails -AppCount $appCount -TweakCount $tweakCount -DeploymentSettings @($config.Deployment)
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Import Configuration' -Prompt 'Select which settings to import:' -Categories $availableCategories -CategoryDetails $categoryDetails
if (-not $categories) { return }
if ($categories -contains 'Applications' -and $config.Apps) {

View File

@@ -1,6 +1,72 @@
function Show-MainWindow {
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase,System.Windows.Forms | Out-Null
# Helper to constrain the maximized window to the monitor working area (respects taskbar).
# Required for WindowStyle=None windows — without this the window extends behind the taskbar.
if (-not ([System.Management.Automation.PSTypeName]'Win11Debloat.MaximizedWindowHelper').Type) {
Add-Type -Namespace Win11Debloat -Name MaximizedWindowHelper `
-ReferencedAssemblies 'PresentationFramework','System.Windows.Forms','System.Drawing' `
-MemberDefinition @'
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
private struct MINMAXINFO {
public POINT ptReserved, ptMaxSize, ptMaxPosition, ptMinTrackSize, ptMaxTrackSize;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
private struct POINT { public int x, y; }
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern System.IntPtr MonitorFromWindow(System.IntPtr hwnd, uint dwFlags);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern bool GetMonitorInfo(System.IntPtr hMonitor, ref MONITORINFO lpmi);
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
private struct RECT {
public int Left, Top, Right, Bottom;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private struct MONITORINFO {
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
public static System.IntPtr WmGetMinMaxInfoHook(
System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) {
if (msg == 0x0024) { // WM_GETMINMAXINFO
var mmi = (MINMAXINFO)System.Runtime.InteropServices.Marshal.PtrToStructure(
lParam, typeof(MINMAXINFO));
const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
var monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
var monitorInfo = new MONITORINFO();
monitorInfo.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(MONITORINFO));
if (monitor != System.IntPtr.Zero && GetMonitorInfo(monitor, ref monitorInfo)) {
mmi.ptMaxPosition.x = monitorInfo.rcWork.Left - monitorInfo.rcMonitor.Left;
mmi.ptMaxPosition.y = monitorInfo.rcWork.Top - monitorInfo.rcMonitor.Top;
mmi.ptMaxSize.x = monitorInfo.rcWork.Right - monitorInfo.rcWork.Left;
mmi.ptMaxSize.y = monitorInfo.rcWork.Bottom - monitorInfo.rcWork.Top;
}
else {
var screen = System.Windows.Forms.Screen.FromHandle(hwnd);
var wa = screen.WorkingArea;
var bounds = screen.Bounds;
mmi.ptMaxPosition.x = wa.Left - bounds.Left;
mmi.ptMaxPosition.y = wa.Top - bounds.Top;
mmi.ptMaxSize.x = wa.Width;
mmi.ptMaxSize.y = wa.Height;
}
System.Runtime.InteropServices.Marshal.StructureToPtr(mmi, lParam, true);
}
return System.IntPtr.Zero;
}
'@
}
# Get current Windows build version
$WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild
@@ -19,7 +85,8 @@ function Show-MainWindow {
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
# Get named elements
$titleBar = $window.FindName('TitleBar')
$mainBorder = $window.FindName('MainBorder')
$titleBarBackground = $window.FindName('TitleBarBackground')
$kofiBtn = $window.FindName('KofiBtn')
$menuBtn = $window.FindName('MenuBtn')
$closeBtn = $window.FindName('CloseBtn')
@@ -30,10 +97,118 @@ function Show-MainWindow {
$importConfigBtn = $window.FindName('ImportConfigBtn')
$exportConfigBtn = $window.FindName('ExportConfigBtn')
# Title bar event handlers
$titleBar.Add_MouseLeftButtonDown({
if ($_.OriginalSource -is [System.Windows.Controls.Grid] -or $_.OriginalSource -is [System.Windows.Controls.Border] -or $_.OriginalSource -is [System.Windows.Controls.TextBlock]) {
$window.DragMove()
$windowStateNormal = [System.Windows.WindowState]::Normal
$windowStateMaximized = [System.Windows.WindowState]::Maximized
$normalWindowShadow = $mainBorder.Effect
$initialNormalMaxWidth = 1400.0
$convertScreenPointToDip = {
param(
[double]$x,
[double]$y
)
$source = [System.Windows.PresentationSource]::FromVisual($window)
if ($null -eq $source -or $null -eq $source.CompositionTarget) {
return [System.Windows.Point]::new($x, $y)
}
return $source.CompositionTarget.TransformFromDevice.Transform([System.Windows.Point]::new($x, $y))
}
$convertScreenPixelsToDip = {
param(
[double]$width,
[double]$height
)
$topLeft = & $convertScreenPointToDip 0 0
$bottomRight = & $convertScreenPointToDip $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 = {
$chrome = [System.Windows.Shell.WindowChrome]::GetWindowChrome($window)
if ($window.WindowState -eq $windowStateMaximized) {
$mainBorder.Margin = [System.Windows.Thickness]::new(0)
$mainBorder.BorderThickness = [System.Windows.Thickness]::new(0)
$mainBorder.CornerRadius = [System.Windows.CornerRadius]::new(0)
$mainBorder.Effect = $null
$titleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(0)
# Zero out resize borders when maximized so the entire title bar row is draggable
if ($chrome) { $chrome.ResizeBorderThickness = [System.Windows.Thickness]::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)
if ($chrome) { $chrome.ResizeBorderThickness = [System.Windows.Thickness]::new(5) }
}
}
$applyInitialWindowSize = {
if ($window.WindowState -ne $windowStateNormal) {
return
}
$screen = & $getWindowScreen
if ($null -eq $screen) {
return
}
$workingAreaTopLeftDip = & $convertScreenPointToDip $screen.WorkingArea.Left $screen.WorkingArea.Top
$workingAreaDip = & $convertScreenPixelsToDip $screen.WorkingArea.Width $screen.WorkingArea.Height
$window.Width = [Math]::Min($initialNormalMaxWidth, $workingAreaDip.Width)
$window.Left = $workingAreaTopLeftDip.X + (($workingAreaDip.Width - $window.Width) / 2)
}
$window.Add_SourceInitialized({
& $applyInitialWindowSize
& $updateWindowChrome
# Register WM_GETMINMAXINFO hook so maximizing respects the working area (taskbar)
$hwndHelper = New-Object System.Windows.Interop.WindowInteropHelper($window)
$hwndSource = [System.Windows.Interop.HwndSource]::FromHwnd($hwndHelper.Handle)
$hookMethod = [Win11Debloat.MaximizedWindowHelper].GetMethod('WmGetMinMaxInfoHook')
$hook = [System.Delegate]::CreateDelegate([System.Windows.Interop.HwndSourceHook], $hookMethod)
$hwndSource.AddHook($hook)
})
$contentGrid = $window.FindName('ContentGrid')
$maxContentWidth = 1600.0
$updateContentMargin = {
$w = $window.ActualWidth
if ($w -gt $maxContentWidth) {
$gutter = [Math]::Floor(($w - $maxContentWidth) / 2)
$contentGrid.Margin = [System.Windows.Thickness]::new($gutter, 0, $gutter, 0)
} else {
$contentGrid.Margin = [System.Windows.Thickness]::new(0)
}
}
$window.Add_SizeChanged({ & $updateContentMargin })
$window.Add_StateChanged({
& $updateWindowChrome
})
$window.Add_LocationChanged({
# Nudge the popup offset to force WPF to recalculate its screen position
if ($script:BubblePopup -and $script:BubblePopup.IsOpen) {
$script:BubblePopup.HorizontalOffset += 1
$script:BubblePopup.HorizontalOffset -= 1
}
})
@@ -94,135 +269,6 @@ function Show-MainWindow {
$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
$appsPanel = $window.FindName('AppSelectionPanel')
$onlyInstalledAppsBox = $window.FindName('OnlyInstalledAppsBox')
@@ -241,6 +287,86 @@ function Show-MainWindow {
$jsonPresetsPanel = $window.FindName('JsonPresetsPanel')
$presetsArrow = $window.FindName('PresetsArrow')
$clearAppSelectionBtn = $window.FindName('ClearAppSelectionBtn')
$tweaksPresetsBtn = $window.FindName('TweaksPresetsBtn')
$tweaksPresetsPopup = $window.FindName('TweaksPresetsPopup')
$presetDefaultTweaksBtn = $window.FindName('PresetDefaultTweaksBtn')
$presetLastUsedTweaksBtn = $window.FindName('PresetLastUsedTweaksBtn')
$presetPrivacyTweaksBtn = $window.FindName('PresetPrivacyTweaksBtn')
$presetAITweaksBtn = $window.FindName('PresetAITweaksBtn')
$tweaksPresetsArrow = $window.FindName('TweaksPresetsArrow')
function AttachTriStateClickBehavior {
param([System.Windows.Controls.CheckBox]$checkBox)
if (-not $checkBox -or -not $checkBox.IsThreeState) { return }
if (-not $checkBox.PSObject.Properties['WasIndeterminateBeforeClick']) {
Add-Member -InputObject $checkBox -MemberType NoteProperty -Name 'WasIndeterminateBeforeClick' -Value $false
}
$checkBox.Add_PreviewMouseLeftButtonDown({
$this.WasIndeterminateBeforeClick = ($this.IsChecked -eq [System.Nullable[bool]]$null)
})
}
function NormalizeCheckboxState {
param([System.Windows.Controls.CheckBox]$checkBox)
if ($checkBox.PSObject.Properties['WasIndeterminateBeforeClick'] -and $checkBox.WasIndeterminateBeforeClick) {
# WPF toggles null -> false before Click handlers fire; restore desired mixed -> checked behavior.
$checkBox.WasIndeterminateBeforeClick = $false
$checkBox.IsChecked = $true
return $true
}
return ($checkBox.IsChecked -eq $true)
}
function SetTriStatePresetCheckBoxState {
param(
[System.Windows.Controls.CheckBox]$CheckBox,
[int]$Total,
[int]$Selected
)
if (-not $CheckBox) { return }
if ($Total -eq 0) {
$CheckBox.IsEnabled = $false
$CheckBox.IsChecked = $false
return
}
$CheckBox.IsEnabled = $true
if ($Selected -eq 0) {
$CheckBox.IsChecked = $false
}
elseif ($Selected -eq $Total) {
$CheckBox.IsChecked = $true
}
else {
$CheckBox.IsChecked = [System.Nullable[bool]]$null
}
}
function AnimateDropdownArrow {
param(
[System.Windows.Controls.TextBlock]$arrow,
[double]$angle
)
if (-not $arrow) { return }
$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
$arrow.RenderTransform.BeginAnimation([System.Windows.Media.RotateTransform]::AngleProperty, $animation)
}
# Load JSON-defined presets and build dynamic preset checkboxes
$script:JsonPresetCheckboxes = @()
@@ -249,16 +375,17 @@ function Show-MainWindow {
$checkbox.Content = $preset.Name
$checkbox.IsThreeState = $true
$checkbox.Style = $window.Resources['PresetCheckBoxStyle']
$checkbox.ToolTip = "Select $($preset.Name)"
$checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $preset.Name)
AttachTriStateClickBehavior -checkBox $checkbox
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'PresetAppIds' -Value $preset.AppIds
$jsonPresetsPanel.Children.Add($checkbox) | Out-Null
$script:JsonPresetCheckboxes += $checkbox
$checkbox.Add_Click({
if ($script:UpdatingPresets) { return }
$check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
$presetIds = $this.PresetAppIds
$check = NormalizeCheckboxState -checkBox $this
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $presetIds -contains $_ }).Count -gt 0 }.GetNewClosure() -Check $check
})
}
@@ -283,6 +410,7 @@ function Show-MainWindow {
# Guard flag to prevent preset handlers from firing when we update their state programmatically
$script:UpdatingPresets = $false
$script:UpdatingTweakPresets = $false
# Sort state for the app table
$script:SortColumn = 'Name'
@@ -413,19 +541,7 @@ function Show-MainWindow {
}
}
}
if ($total -eq 0) {
$checkbox.IsChecked = $false
$checkbox.IsEnabled = $false
} else {
$checkbox.IsEnabled = $true
if ($checked -eq 0) {
$checkbox.IsChecked = $false
} elseif ($checked -eq $total) {
$checkbox.IsChecked = $true
} else {
$checkbox.IsChecked = [System.Nullable[bool]]$null
}
}
SetTriStatePresetCheckBoxState -CheckBox $checkbox -Total $total -Selected $checked
}
SetPresetState $presetDefaultApps { param($c) $c.SelectedByDefault -eq $true }
@@ -590,8 +706,8 @@ function Show-MainWindow {
$helpBtn.Tag = (GetWikiUrlForCategory -category $categoryName)
$helpBtn.Style = $window.Resources['CategoryHelpLinkButtonStyle']
$helpBtn.Add_Click({
param($sender, $e)
if ($sender.Tag) { Start-Process $sender.Tag }
param($button, $e)
if ($button.Tag) { Start-Process $button.Tag }
})
$headerRow.Children.Add($helpBtn) | Out-Null
@@ -701,7 +817,7 @@ function Show-MainWindow {
try { $lblBorderObj = $window.FindName("$comboName`_LabelBorder") } catch {}
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
}
$script:UiControlMappings[$comboName] = @{ Type='group'; Values = $group.Values; Label = $group.Label }
$script:UiControlMappings[$comboName] = @{ Type='group'; Values = $group.Values; Label = $group.Label; Category = $categoryName }
}
elseif ($item.Type -eq 'feature') {
$feature = $item.Data
@@ -721,7 +837,7 @@ function Show-MainWindow {
try { $lblBorderObj = $window.FindName("$comboName`_LabelBorder") } catch {}
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
}
$script:UiControlMappings[$comboName] = @{ Type='feature'; FeatureId = $feature.FeatureId; Action = $feature.Action }
$script:UiControlMappings[$comboName] = @{ Type='feature'; FeatureId = $feature.FeatureId; Action = $feature.Action; Category = $categoryName }
}
}
}
@@ -918,37 +1034,45 @@ function Show-MainWindow {
# Animate arrow when popup opens/closes, and lazily update preset states
$presetsPopup.Add_Opened({
UpdatePresetStates
$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)
AnimateDropdownArrow -arrow $presetsArrow -angle 180
})
$presetsPopup.Add_Closed({
$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)
AnimateDropdownArrow -arrow $presetsArrow -angle 0
$presetsBtn.IsChecked = $false
})
$tweaksPresetsPopup.Add_Opened({
UpdateTweakPresetStates
AnimateDropdownArrow -arrow $tweaksPresetsArrow -angle 180
})
$tweaksPresetsPopup.Add_Closed({
AnimateDropdownArrow -arrow $tweaksPresetsArrow -angle 0
$tweaksPresetsBtn.IsChecked = $false
})
# Close popup when clicking anywhere outside the popup or the presets button.
$window.Add_PreviewMouseDown({
if (-not $presetsPopup.IsOpen) { return }
if ($presetsPopup.Child -ne $null -and $presetsPopup.Child.IsMouseOver) { return }
$isAppPopupOpen = $presetsPopup.IsOpen
$isTweaksPopupOpen = $tweaksPresetsPopup.IsOpen
if (-not $isAppPopupOpen -and -not $isTweaksPopupOpen) { return }
if ($isAppPopupOpen -and $null -ne $presetsPopup.Child -and $presetsPopup.Child.IsMouseOver) { return }
if ($isTweaksPopupOpen -and $null -ne $tweaksPresetsPopup.Child -and $tweaksPresetsPopup.Child.IsMouseOver) { return }
$src = $_.OriginalSource -as [System.Windows.DependencyObject]
if ($src -ne $null) {
$inBtn = $presetsBtn.IsAncestorOf($src) -or [System.Object]::ReferenceEquals($presetsBtn, $src)
if (-not $inBtn) { $presetsPopup.IsOpen = $false }
if ($null -ne $src) {
$inAppBtn = $presetsBtn.IsAncestorOf($src) -or [System.Object]::ReferenceEquals($presetsBtn, $src)
$inTweaksBtn = $tweaksPresetsBtn.IsAncestorOf($src) -or [System.Object]::ReferenceEquals($tweaksPresetsBtn, $src)
if ($isAppPopupOpen -and -not $inAppBtn) { $presetsPopup.IsOpen = $false }
if ($isTweaksPopupOpen -and -not $inTweaksBtn) { $tweaksPresetsPopup.IsOpen = $false }
}
})
# Close the preset menu when the main window loses focus (e.g., user switches to another app).
$window.Add_Deactivated({
if ($presetsPopup.IsOpen) { $presetsPopup.IsOpen = $false }
if ($tweaksPresetsPopup.IsOpen) { $tweaksPresetsPopup.IsOpen = $false }
})
# Toggle popup on button click
@@ -957,11 +1081,26 @@ function Show-MainWindow {
$presetsBtn.IsChecked = $presetsPopup.IsOpen
})
$tweaksPresetsBtn.Add_Click({
$tweaksPresetsPopup.IsOpen = -not $tweaksPresetsPopup.IsOpen
$tweaksPresetsBtn.IsChecked = $tweaksPresetsPopup.IsOpen
})
foreach ($presetCheckBox in @(
$presetDefaultApps,
$presetLastUsed,
$presetDefaultTweaksBtn,
$presetLastUsedTweaksBtn,
$presetPrivacyTweaksBtn,
$presetAITweaksBtn
)) {
AttachTriStateClickBehavior -checkBox $presetCheckBox
}
# Preset: Default selection
$presetDefaultApps.Add_Click({
if ($script:UpdatingPresets) { return }
$check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
$check = NormalizeCheckboxState -checkBox $this
ApplyPresetToApps -MatchFilter { param($c) $c.SelectedByDefault -eq $true } -Check $check
})
@@ -1078,7 +1217,7 @@ function Show-MainWindow {
})
$appSearchBox.Add_KeyDown({
param($sender, $e)
param($sourceControl, $e)
if ($e.Key -eq [System.Windows.Input.Key]::Enter -and $script:AppSearchMatches.Count -gt 0) {
# Reset background of current active match
$script:AppSearchMatches[$script:AppSearchMatchIndex].Background = $window.Resources["SearchHighlightColor"]
@@ -1201,7 +1340,7 @@ function Show-MainWindow {
# Add Ctrl+F keyboard shortcut to focus search box on current tab
$window.Add_KeyDown({
param($sender, $e)
param($sourceControl, $e)
# Check if Ctrl+F was pressed
if ($e.Key -eq [System.Windows.Input.Key]::F -and
@@ -1728,6 +1867,9 @@ function Show-MainWindow {
# Initialize UI elements on window load
$window.Add_Loaded({
BuildDynamicTweaks
RefreshTweakPresetSources -defaultSettingsJson $defaultsJson -lastUsedSettingsJson $lastUsedSettingsJson
RegisterTweakPresetControlStateHandlers
UpdateTweakPresetStates
LoadAppsIntoMainUI
@@ -1770,61 +1912,279 @@ function Show-MainWindow {
UpdateNavigationButtons
})
# Handle Load Defaults button
$loadDefaultsBtn = $window.FindName('LoadDefaultsBtn')
$loadDefaultsBtn.Add_Click({
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
function BuildTweakPresetControlMap {
param($settingsJson)
if (-not $defaultsJson) {
Show-MessageBox -Message "Failed to load default settings file" -Title "Error" -Button 'OK' -Icon 'Error'
return
$presetMap = @{}
if (-not $settingsJson -or -not $settingsJson.Settings -or -not $script:UiControlMappings) {
return $presetMap
}
ApplySettingsToUiControls -window $window -settingsJson $defaultsJson -uiControlMappings $script:UiControlMappings
})
# FeatureId -> control metadata, similar to ApplySettingsToUiControls lookup.
$featureIdIndex = @{}
foreach ($controlName in $script:UiControlMappings.Keys) {
$control = $window.FindName($controlName)
if (-not $control -or $control.Visibility -ne 'Visible') { continue }
# Handle Load Last Used settings and Load Last Used apps
$loadLastUsedBtn = $window.FindName('LoadLastUsedBtn')
$mapping = $script:UiControlMappings[$controlName]
if ($mapping.Type -eq 'group') {
$i = 1
foreach ($val in $mapping.Values) {
foreach ($fid in $val.FeatureIds) {
$featureIdIndex[$fid] = @{ ControlName = $controlName; Control = $control; MappingType = 'group'; Index = $i }
}
$i++
}
}
elseif ($mapping.Type -eq 'feature') {
$featureIdIndex[$mapping.FeatureId] = @{ ControlName = $controlName; Control = $control; MappingType = 'feature' }
}
}
foreach ($setting in $settingsJson.Settings) {
if ($setting.Value -ne $true) { continue }
if ($setting.Name -eq 'CreateRestorePoint') { continue }
$entry = $featureIdIndex[$setting.Name]
if (-not $entry) { continue }
if ($presetMap.ContainsKey($entry.ControlName)) { continue }
$controlType = if ($entry.Control -is [System.Windows.Controls.CheckBox]) { 'CheckBox' } else { 'ComboBox' }
$desiredValue = switch ($entry.MappingType) {
'group' { $entry.Index }
default { if ($controlType -eq 'CheckBox') { $true } else { 1 } }
}
$presetMap[$entry.ControlName] = @{ Control = $entry.Control; ControlType = $controlType; DesiredValue = $desiredValue }
}
return $presetMap
}
function BuildCategoryTweakPresetMap {
param([string]$Category)
$presetMap = @{}
if (-not $script:UiControlMappings) { return $presetMap }
foreach ($controlName in $script:UiControlMappings.Keys) {
$mapping = $script:UiControlMappings[$controlName]
if ($mapping.Category -ne $Category) { continue }
$control = $window.FindName($controlName)
if (-not $control -or $control.Visibility -ne 'Visible') { continue }
$controlType = if ($control -is [System.Windows.Controls.CheckBox]) { 'CheckBox' } else { 'ComboBox' }
$desiredValue = if ($controlType -eq 'CheckBox') { $true } else { 1 }
$presetMap[$controlName] = @{ Control = $control; ControlType = $controlType; DesiredValue = $desiredValue }
}
return $presetMap
}
function GetSavedAppIdsFromSettingsJson {
param($settingsJson)
if (-not $settingsJson -or -not $settingsJson.Settings) {
return $null
}
$appsValue = $null
foreach ($setting in $settingsJson.Settings) {
if ($setting.Name -eq 'Apps' -and $setting.Value) {
$appsValue = $setting.Value
break
}
}
if (-not $appsValue) {
return $null
}
$savedAppIds = @()
if ($appsValue -is [string]) {
$savedAppIds = $appsValue.Split(',')
}
elseif ($appsValue -is [array]) {
$savedAppIds = $appsValue
}
$savedAppIds = $savedAppIds | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
if ($savedAppIds.Count -eq 0) {
return $null
}
return $savedAppIds
}
function ApplyTweakPresetMap {
param(
[hashtable]$PresetMap,
[bool]$Check
)
if (-not $PresetMap) {
$PresetMap = @{}
}
$wasUpdatingTweakPresets = [bool]$script:UpdatingTweakPresets
$script:UpdatingTweakPresets = $true
try {
foreach ($target in $PresetMap.Values) {
$control = $target.Control
if (-not $control) { continue }
if ($target.ControlType -eq 'CheckBox') {
$control.IsChecked = $Check
}
elseif ($target.ControlType -eq 'ComboBox') {
$desiredIndex = [int]$target.DesiredValue
if ($Check) {
$control.SelectedIndex = $desiredIndex
}
elseif ($control.SelectedIndex -eq $desiredIndex) {
$control.SelectedIndex = 0
}
}
}
}
finally {
$script:UpdatingTweakPresets = $wasUpdatingTweakPresets
}
if (-not $wasUpdatingTweakPresets) {
UpdateTweakPresetStates
}
}
function SetTweakPresetState {
param(
[System.Windows.Controls.CheckBox]$PresetCheckBox,
[hashtable]$PresetMap
)
if (-not $PresetCheckBox) { return }
if (-not $PresetMap) {
$PresetMap = @{}
}
$total = $PresetMap.Count
$selected = 0
foreach ($target in $PresetMap.Values) {
$control = $target.Control
if (-not $control) { continue }
if ($target.ControlType -eq 'CheckBox' -and $control.IsChecked -eq $true) {
$selected++
}
elseif ($target.ControlType -eq 'ComboBox' -and $control.SelectedIndex -eq [int]$target.DesiredValue) {
$selected++
}
}
SetTriStatePresetCheckBoxState -CheckBox $PresetCheckBox -Total $total -Selected $selected
}
function UpdateTweakPresetStates {
$script:UpdatingTweakPresets = $true
try {
SetTweakPresetState -PresetCheckBox $presetDefaultTweaksBtn -PresetMap $script:DefaultTweakPresetMap
if ($presetLastUsedTweaksBtn -and $presetLastUsedTweaksBtn.Visibility -ne 'Collapsed') {
SetTweakPresetState -PresetCheckBox $presetLastUsedTweaksBtn -PresetMap $script:LastUsedTweakPresetMap
}
SetTweakPresetState -PresetCheckBox $presetPrivacyTweaksBtn -PresetMap $script:PrivacyTweakPresetMap
SetTweakPresetState -PresetCheckBox $presetAITweaksBtn -PresetMap $script:AITweakPresetMap
}
finally {
$script:UpdatingTweakPresets = $false
}
}
function RegisterTweakPresetControlStateHandlers {
if (-not $script:UiControlMappings) { return }
foreach ($controlName in $script:UiControlMappings.Keys) {
$control = $window.FindName($controlName)
if (-not $control) { continue }
if ($control -is [System.Windows.Controls.CheckBox]) {
$control.Add_Checked({ if (-not $script:UpdatingTweakPresets) { UpdateTweakPresetStates } })
$control.Add_Unchecked({ if (-not $script:UpdatingTweakPresets) { UpdateTweakPresetStates } })
}
elseif ($control -is [System.Windows.Controls.ComboBox]) {
$control.Add_SelectionChanged({ if (-not $script:UpdatingTweakPresets) { UpdateTweakPresetStates } })
}
}
}
function RefreshTweakPresetSources {
param(
$defaultSettingsJson,
$lastUsedSettingsJson
)
$script:DefaultTweakPresetMap = BuildTweakPresetControlMap -settingsJson $defaultSettingsJson
$script:LastUsedTweakPresetMap = BuildTweakPresetControlMap -settingsJson $lastUsedSettingsJson
$script:PrivacyTweakPresetMap = BuildCategoryTweakPresetMap -Category 'Privacy & Suggested Content'
$script:AITweakPresetMap = BuildCategoryTweakPresetMap -Category 'AI'
if ($presetLastUsedTweaksBtn) {
$presetLastUsedTweaksBtn.Visibility = if ($script:LastUsedTweakPresetMap.Count -gt 0) { 'Visible' } else { 'Collapsed' }
}
}
$lastUsedSettingsJson = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
$hasSettings = $false
$appsSetting = $null
if ($lastUsedSettingsJson -and $lastUsedSettingsJson.Settings) {
foreach ($s in $lastUsedSettingsJson.Settings) {
# Only count as hasSettings if a setting other than RemoveApps/Apps is present and true
if ($s.Value -eq $true -and $s.Name -ne 'RemoveApps' -and $s.Name -ne 'Apps') { $hasSettings = $true }
if ($s.Name -eq 'Apps' -and $s.Value) { $appsSetting = $s.Value }
}
}
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
$script:DefaultTweakPresetMap = @{}
$script:LastUsedTweakPresetMap = @{}
$script:PrivacyTweakPresetMap = @{}
$script:AITweakPresetMap = @{}
$script:SavedAppIds = GetSavedAppIdsFromSettingsJson -settingsJson $lastUsedSettingsJson
# Show option to load last used settings if they exist
if ($hasSettings) {
$loadLastUsedBtn.Add_Click({
try {
ApplySettingsToUiControls -window $window -settingsJson $lastUsedSettingsJson -uiControlMappings $script:UiControlMappings
}
catch {
Show-MessageBox -Message "Failed to load last used settings: $_" -Title "Error" -Button 'OK' -Icon 'Error'
}
if ($presetDefaultTweaksBtn) {
$presetDefaultTweaksBtn.Add_Click({
if ($script:UpdatingTweakPresets) { return }
$check = NormalizeCheckboxState -checkBox $this
ApplyTweakPresetMap -PresetMap $script:DefaultTweakPresetMap -Check $check
})
}
else {
$loadLastUsedBtn.Visibility = 'Collapsed'
if ($presetLastUsedTweaksBtn) {
$presetLastUsedTweaksBtn.Add_Click({
if ($script:UpdatingTweakPresets) { return }
$check = NormalizeCheckboxState -checkBox $this
ApplyTweakPresetMap -PresetMap $script:LastUsedTweakPresetMap -Check $check
})
}
if ($presetPrivacyTweaksBtn) {
$presetPrivacyTweaksBtn.Add_Click({
if ($script:UpdatingTweakPresets) { return }
$check = NormalizeCheckboxState -checkBox $this
ApplyTweakPresetMap -PresetMap $script:PrivacyTweakPresetMap -Check $check
})
}
if ($presetAITweaksBtn) {
$presetAITweaksBtn.Add_Click({
if ($script:UpdatingTweakPresets) { return }
$check = NormalizeCheckboxState -checkBox $this
ApplyTweakPresetMap -PresetMap $script:AITweakPresetMap -Check $check
})
}
# Hide Last used tweak preset by default; it is shown after dynamic controls are built and mappings are resolved.
if ($presetLastUsedTweaksBtn) {
$presetLastUsedTweaksBtn.Visibility = 'Collapsed'
}
# Preset: Last used selection (wired to PresetLastUsed checkbox)
if ($appsSetting -and $appsSetting.ToString().Trim().Length -gt 0) {
# Parse and store saved app IDs for UpdatePresetStates
$script:SavedAppIds = @()
if ($appsSetting -is [string]) { $script:SavedAppIds = $appsSetting.Split(',') }
elseif ($appsSetting -is [array]) { $script:SavedAppIds = $appsSetting }
$script:SavedAppIds = $script:SavedAppIds | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
if ($script:SavedAppIds) {
$presetLastUsed.Add_Click({
if ($script:UpdatingPresets) { return }
$check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
$check = NormalizeCheckboxState -checkBox $this
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 } -Check $check
})
}
@@ -1848,6 +2208,7 @@ function Show-MainWindow {
}
}
}
UpdateTweakPresetStates
})
# Preload app data to speed up loading when user navigates to App Removal tab
@@ -1859,5 +2220,12 @@ function Show-MainWindow {
}
# Show the window
return $window.ShowDialog()
$frame = [System.Windows.Threading.DispatcherFrame]::new()
$window.Add_Closed({
$frame.Continue = $false
})
$window.Show() | Out-Null
[System.Windows.Threading.Dispatcher]::PushFrame($frame)
return $null
}

225
Scripts/Get-Dev.ps1 Normal file
View File

@@ -0,0 +1,225 @@
param (
[switch]$CLI,
[switch]$Silent,
[switch]$Verbose,
[switch]$Sysprep,
[string]$LogPath,
[string]$User,
[switch]$NoRestartExplorer,
[switch]$CreateRestorePoint,
[switch]$RunAppsListGenerator,
[switch]$RunDefaults,
[switch]$RunDefaultsLite,
[switch]$RunSavedSettings,
[string]$Config,
[string]$Apps,
[string]$AppRemovalTarget,
[switch]$RemoveApps,
[switch]$RemoveAppsCustom,
[switch]$RemoveGamingApps,
[switch]$RemoveCommApps,
[switch]$RemoveHPApps,
[switch]$RemoveW11Outlook,
[switch]$ForceRemoveEdge,
[switch]$DisableDVR,
[switch]$DisableGameBarIntegration,
[switch]$EnableWindowsSandbox,
[switch]$EnableWindowsSubsystemForLinux,
[switch]$DisableTelemetry,
[switch]$DisableSearchHistory,
[switch]$DisableFastStartup,
[switch]$DisableBitlockerAutoEncryption,
[switch]$DisableModernStandbyNetworking,
[switch]$DisableStorageSense,
[switch]$DisableUpdateASAP,
[switch]$PreventUpdateAutoReboot,
[switch]$DisableDeliveryOptimization,
[switch]$DisableBing,
[switch]$DisableStoreSearchSuggestions,
[switch]$DisableDesktopSpotlight,
[switch]$DisableLockscreenTips,
[switch]$DisableSuggestions,
[switch]$DisableLocationServices,
[switch]$DisableFindMyDevice,
[switch]$DisableEdgeAds,
[switch]$DisableBraveBloat,
[switch]$DisableSettings365Ads,
[switch]$DisableSettingsHome,
[switch]$ShowHiddenFolders,
[switch]$ShowKnownFileExt,
[switch]$HideDupliDrive,
[switch]$EnableDarkMode,
[switch]$DisableTransparency,
[switch]$DisableAnimations,
[switch]$TaskbarAlignLeft,
[switch]$CombineTaskbarAlways, [switch]$CombineTaskbarWhenFull, [switch]$CombineTaskbarNever,
[switch]$CombineMMTaskbarAlways, [switch]$CombineMMTaskbarWhenFull, [switch]$CombineMMTaskbarNever,
[switch]$MMTaskbarModeAll, [switch]$MMTaskbarModeMainActive, [switch]$MMTaskbarModeActive,
[switch]$HideSearchTb, [switch]$ShowSearchIconTb, [switch]$ShowSearchLabelTb, [switch]$ShowSearchBoxTb,
[switch]$HideTaskview,
[switch]$DisableStartRecommended,
[switch]$DisableStartAllApps,
[switch]$DisableStartPhoneLink,
[switch]$DisableCopilot,
[switch]$DisableRecall,
[switch]$DisableClickToDo,
[switch]$DisableAISvcAutoStart,
[switch]$DisablePaintAI,
[switch]$DisableNotepadAI,
[switch]$DisableEdgeAI,
[switch]$DisableSearchHighlights,
[switch]$DisableWidgets,
[switch]$HideChat,
[switch]$EnableEndTask,
[switch]$EnableLastActiveClick,
[switch]$ClearStart,
[string]$ReplaceStart,
[switch]$ClearStartAllUsers,
[string]$ReplaceStartAllUsers,
[switch]$RevertContextMenu,
[switch]$DisableDragTray,
[switch]$DisableMouseAcceleration,
[switch]$DisableStickyKeys,
[switch]$DisableWindowSnapping,
[switch]$DisableSnapAssist,
[switch]$DisableSnapLayouts,
[switch]$HideTabsInAltTab, [switch]$Show3TabsInAltTab, [switch]$Show5TabsInAltTab, [switch]$Show20TabsInAltTab,
[switch]$HideHome,
[switch]$HideGallery,
[switch]$ExplorerToHome,
[switch]$ExplorerToThisPC,
[switch]$ExplorerToDownloads,
[switch]$ExplorerToOneDrive,
[switch]$AddFoldersToThisPC,
[switch]$HideOnedrive,
[switch]$Hide3dObjects,
[switch]$HideMusic,
[switch]$HideIncludeInLibrary,
[switch]$HideGiveAccessTo,
[switch]$HideShare,
[switch]$ShowDriveLettersFirst,
[switch]$ShowDriveLettersLast,
[switch]$ShowNetworkDriveLettersFirst,
[switch]$HideDriveLetters
)
# Show error if current powershell environment does not have LanguageMode set to FullLanguage
if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") {
Write-Host "Error: Win11Debloat is unable to run on your system. PowerShell execution is restricted by security policies" -ForegroundColor Red
Write-Output ""
Write-Output "Press enter to exit..."
Read-Host | Out-Null
Exit
}
Clear-Host
Write-Output "-------------------------------------------------------------------------------------------"
Write-Output " Win11Debloat Script - Get Dev"
Write-Output "-------------------------------------------------------------------------------------------"
Write-Output "> Downloading Win11Debloat for development..."
# Download latest version of Win11Debloat from GitHub master branch as zip archive
try {
Invoke-RestMethod "https://github.com/Raphire/Win11Debloat/archive/refs/heads/master.zip" -OutFile "$env:TEMP/win11debloat.zip"
}
catch {
Write-Host "Error: Unable to fetch master branch from GitHub. Please check your internet connection and try again." -ForegroundColor Red
Write-Output ""
Write-Output "Press enter to exit..."
Read-Host | Out-Null
Exit
}
Write-Output ""
Write-Output "> Cleaning up old Win11Debloat folder..."
# Remove old script folder if it exists, but keep config and log files
if (Test-Path "$env:TEMP/Win11Debloat") {
Get-ChildItem -Path "$env:TEMP/Win11Debloat" -Exclude CustomAppsList,LastUsedSettings.json,Win11Debloat.log,Config,Logs | Remove-Item -Recurse -Force
}
$configDir = "$env:TEMP/Win11Debloat/Config"
$backupDir = "$env:TEMP/Win11Debloat/ConfigOld"
# Temporarily move existing config files if they exist to prevent them from being overwritten by the new script files, will be moved back after the new script is unpacked
if (Test-Path "$configDir") {
New-Item -ItemType Directory -Path "$backupDir" -Force | Out-Null
$filesToKeep = @(
'CustomAppsList',
'LastUsedSettings.json'
)
Get-ChildItem -Path "$configDir" -Recurse | Where-Object { $_.Name -in $filesToKeep } | Move-Item -Destination "$backupDir"
Remove-Item "$configDir" -Recurse -Force
}
Write-Output ""
Write-Output "> Unpacking..."
# Unzip archive to Win11Debloat folder
Expand-Archive "$env:TEMP/win11debloat.zip" "$env:TEMP/Win11Debloat"
# Remove archive
Remove-Item "$env:TEMP/win11debloat.zip"
# Move files
Get-ChildItem -Path "$env:TEMP/Win11Debloat/*Win11Debloat-*" -Recurse | Move-Item -Destination "$env:TEMP/Win11Debloat"
# Add existing config files back to Config folder
if (Test-Path "$backupDir") {
if (-not (Test-Path "$configDir")) {
New-Item -ItemType Directory -Path "$configDir" -Force | Out-Null
}
Get-ChildItem -Path "$backupDir" -Recurse | Move-Item -Destination "$configDir"
Remove-Item "$backupDir" -Recurse -Force
}
# Make list of arguments to pass on to the script
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
if ($_.Value -eq $true) {
"-$($_.Key)"
}
else {
"-$($_.Key) ""$($_.Value)"""
}
})
Write-Output ""
Write-Output "> Launching Win11Debloat..."
# Minimize the powershell window when no parameters are provided
if ($arguments.Count -eq 0) {
$windowStyle = "Minimized"
}
else {
$windowStyle = "Normal"
}
# Remove Powershell 7 modules from path to prevent module loading issues in the script
if ($PSVersionTable.PSVersion.Major -ge 7) {
$NewPSModulePath = $env:PSModulePath -split ';' | Where-Object -FilterScript { $_ -like '*WindowsPowerShell*' }
$env:PSModulePath = $NewPSModulePath -join ';'
}
# Run Win11Debloat script with the provided arguments
$debloatProcess = Start-Process powershell.exe -WindowStyle $windowStyle -PassThru -ArgumentList "-executionpolicy bypass -File $env:TEMP\Win11Debloat\Win11Debloat.ps1 $arguments" -Verb RunAs
# Wait for the process to finish before continuing
if ($null -ne $debloatProcess) {
$debloatProcess.WaitForExit()
}
# Remove all remaining script files, except for CustomAppsList and LastUsedSettings.json files
if (Test-Path "$env:TEMP/Win11Debloat") {
Write-Output ""
Write-Output "> Cleaning up..."
# Cleanup, remove Win11Debloat directory
Get-ChildItem -Path "$env:TEMP/Win11Debloat" -Exclude CustomAppsList,LastUsedSettings.json,Win11Debloat.log,Win11Debloat-Run.log,Config,Logs | Remove-Item -Recurse -Force
}
Write-Output ""

View File

@@ -97,7 +97,11 @@ param (
[switch]$HideMusic,
[switch]$HideIncludeInLibrary,
[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
@@ -187,7 +191,7 @@ $arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
})
Write-Output ""
Write-Output "> Running Win11Debloat..."
Write-Output "> Launching Win11Debloat..."
# Minimize the powershell window when no parameters are provided
if ($arguments.Count -eq 0) {

View File

@@ -1,5 +1,3 @@
#Requires -RunAsAdministrator
[CmdletBinding(SupportsShouldProcess)]
param (
[switch]$CLI,
@@ -99,13 +97,52 @@ param (
[switch]$HideMusic,
[switch]$HideIncludeInLibrary,
[switch]$HideGiveAccessTo,
[switch]$HideShare
[switch]$HideShare,
[switch]$ShowDriveLettersFirst,
[switch]$ShowDriveLettersLast,
[switch]$ShowNetworkDriveLettersFirst,
[switch]$HideDriveLetters
)
# Check if script is running as administrator
$isAdmin = ([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
# If script is not running as administrator ask user if they want to allow it
if (-not $isAdmin) {
Write-Host "Win11Debloat must be run as Administrator." -ForegroundColor Red
$choice = Read-Host "Restart as Administrator? (y/n)"
if ($choice -match '^[Yy]$') {
$elevatedArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $PSCommandPath)
foreach ($paramName in $PSBoundParameters.Keys) {
$paramValue = $PSBoundParameters[$paramName]
if ($paramValue -is [System.Management.Automation.SwitchParameter]) {
if ($paramValue.IsPresent) {
$elevatedArgs += "-$paramName"
}
}
else {
$elevatedArgs += "-$paramName"
$elevatedArgs += "$paramValue"
}
}
if ($MyInvocation.UnboundArguments.Count -gt 0) {
$elevatedArgs += $MyInvocation.UnboundArguments
}
Start-Process powershell -ArgumentList $elevatedArgs -Verb RunAs
}
exit
}
# Define script-level variables & paths
$script:Version = "2026.03.15"
$script:Version = "2026.04.05"
$script:FeaturesConfigVersion = "2.0"
$script:AppsListFilePath = "$PSScriptRoot/Config/Apps.json"
$script:DefaultSettingsFilePath = "$PSScriptRoot/Config/DefaultSettings.json"
@@ -142,8 +179,16 @@ if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") {
Exit
}
# Display ASCII art launch logo in CLI
Clear-Host
# Ensure required Windows command paths are present in PATH for this session.
$system32Path = "$env:SystemRoot\System32"
if ($env:PATH -notmatch "(?i)(^|;)$([regex]::Escape($system32Path))(?=;|$)") {
$env:PATH = "$env:SystemRoot\System32;$env:SystemRoot;" + $env:PATH
Write-Warning "System32 path was missing from PATH environment variable, it has been added for this session."
}
# Display ASCII art launch logo in CLI
Write-Host ""
Write-Host ""
Write-Host " " -NoNewline; Write-Host " ^" -ForegroundColor Blue