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

@@ -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
})
# Handle Load Last Used settings and Load Last Used apps
$loadLastUsedBtn = $window.FindName('LoadLastUsedBtn')
# 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 }
$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) {