mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-04-03 22:16:30 +00:00
Add option to revert previous changes to windows defaults
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# Prints all pending changes that will be made by the script
|
||||
function PrintPendingChanges {
|
||||
$skippedParams = @()
|
||||
$undoChanges = $script:Params.ContainsKey('Undo')
|
||||
Write-Output "Win11Debloat will make the following changes:"
|
||||
|
||||
if ($script:Params['CreateRestorePoint']) {
|
||||
@@ -9,6 +11,17 @@ function PrintPendingChanges {
|
||||
if ($script:ControlParams -contains $parameterName) {
|
||||
continue
|
||||
}
|
||||
if ($parameterName -eq 'Apps' -or $parameterName -eq 'CreateRestorePoint') {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($undoChanges) {
|
||||
$undoFeature = GetUndoFeatureForParam -paramKey $parameterName
|
||||
if (-not $undoFeature) {
|
||||
$skippedParams += $parameterName
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# Print parameter description
|
||||
switch ($parameterName) {
|
||||
@@ -46,9 +59,19 @@ function PrintPendingChanges {
|
||||
}
|
||||
default {
|
||||
if ($script:Features -and $script:Features.ContainsKey($parameterName)) {
|
||||
$action = $script:Features[$parameterName].Action
|
||||
$action = if ($undoChanges -and $script:Features[$parameterName].UndoAction) {
|
||||
$script:Features[$parameterName].UndoAction
|
||||
}
|
||||
else {
|
||||
$script:Features[$parameterName].Action
|
||||
}
|
||||
$message = $script:Features[$parameterName].Label
|
||||
Write-Output "- $action $message"
|
||||
if ($action) {
|
||||
Write-Output "- $action $message"
|
||||
}
|
||||
else {
|
||||
Write-Output "- $message"
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Fallback: show the parameter name if no feature description is available
|
||||
@@ -59,6 +82,18 @@ function PrintPendingChanges {
|
||||
}
|
||||
}
|
||||
|
||||
if ($undoChanges -and $skippedParams.Count -gt 0) {
|
||||
Write-Output ""
|
||||
Write-Output "The following changes cannot be automatically undone and will be skipped:"
|
||||
|
||||
$uniqueSkipped = $skippedParams | Sort-Object -Unique
|
||||
foreach ($skippedParam in $uniqueSkipped) {
|
||||
$action = $script:Features[$skippedParam].Action
|
||||
$message = $script:Features[$skippedParam].Label
|
||||
Write-Output "- $action $message"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output ""
|
||||
Write-Output ""
|
||||
Write-Output "Press enter to execute the script or press CTRL+C to quit..."
|
||||
|
||||
@@ -11,6 +11,34 @@ function ExecuteParameter {
|
||||
if ($script:Features.ContainsKey($paramKey)) {
|
||||
$feature = $script:Features[$paramKey]
|
||||
}
|
||||
|
||||
$undoChanges = $script:Params.ContainsKey('Undo')
|
||||
$undoFeature = if ($undoChanges) { GetUndoFeatureForParam -paramKey $paramKey } else { $null }
|
||||
|
||||
# In global undo mode, skip any parameter that does not define undo metadata.
|
||||
if ($undoChanges -and -not $undoFeature) {
|
||||
return
|
||||
}
|
||||
|
||||
# If this feature was requested in undo mode, use undo metadata from Features.json.
|
||||
if ($undoChanges -and $undoFeature) {
|
||||
$undoRegFile = $undoFeature.RegistryUndoKey
|
||||
$usesOfflineHive = $script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User")
|
||||
$undoFolderPath = if ($usesOfflineHive) {
|
||||
Join-Path $script:RegfilesPath (Join-Path 'Sysprep' (Join-Path 'Undo' $undoRegFile))
|
||||
}
|
||||
else {
|
||||
Join-Path $script:RegfilesPath (Join-Path 'Undo' $undoRegFile)
|
||||
}
|
||||
|
||||
# Prefer dedicated Undo subfolder files when present, with fallback to legacy root location.
|
||||
if (Test-Path $undoFolderPath) {
|
||||
$undoRegFile = Join-Path 'Undo' $undoRegFile
|
||||
}
|
||||
|
||||
ImportRegistryFile "> $($undoFeature.UndoAction) $($undoFeature.Label)" $undoRegFile
|
||||
return
|
||||
}
|
||||
|
||||
# If feature has RegistryKey and ApplyText, use dynamic ImportRegistryFile
|
||||
if ($feature -and $feature.RegistryKey -and $feature.ApplyText) {
|
||||
@@ -139,13 +167,41 @@ function ExecuteParameter {
|
||||
# Executes all selected parameters/features
|
||||
function ExecuteAllChanges {
|
||||
# Build list of actionable parameters (skip control params and data-only params)
|
||||
$undoChanges = $script:Params.ContainsKey('Undo')
|
||||
$actionableKeys = @()
|
||||
$paramsToRemove = @()
|
||||
foreach ($paramKey in $script:Params.Keys) {
|
||||
if ($script:ControlParams -contains $paramKey) { continue }
|
||||
if ($paramKey -eq 'Apps') { continue }
|
||||
if ($paramKey -eq 'CreateRestorePoint') { continue }
|
||||
|
||||
if ($undoChanges) {
|
||||
$undoFeature = GetUndoFeatureForParam -paramKey $paramKey
|
||||
if (-not $undoFeature) {
|
||||
$paramsToRemove += $paramKey
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
$actionableKeys += $paramKey
|
||||
}
|
||||
|
||||
if ($undoChanges -and $paramsToRemove.Count -gt 0) {
|
||||
foreach ($paramKey in ($paramsToRemove | Sort-Object -Unique)) {
|
||||
if ($script:Params.ContainsKey($paramKey)) {
|
||||
$null = $script:Params.Remove($paramKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If no undo-capable changes remain, disable explorer restart for this run.
|
||||
if ($undoChanges -and $actionableKeys.Count -eq 0) {
|
||||
if (-not $script:Params.ContainsKey('NoRestartExplorer')) {
|
||||
$script:Params['NoRestartExplorer'] = $true
|
||||
}
|
||||
Write-Warning "None of the selected changes can be undone automatically."
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
$totalSteps = $actionableKeys.Count
|
||||
if ($script:Params.ContainsKey("CreateRestorePoint")) { $totalSteps++ }
|
||||
@@ -174,7 +230,10 @@ function ExecuteAllChanges {
|
||||
$stepName = $paramKey
|
||||
if ($script:Features.ContainsKey($paramKey)) {
|
||||
$feature = $script:Features[$paramKey]
|
||||
if ($feature.ApplyText) {
|
||||
if ($undoChanges -and $feature.UndoAction) {
|
||||
$stepName = "$($feature.UndoAction) $($feature.Label)"
|
||||
}
|
||||
elseif ($feature.ApplyText) {
|
||||
# Prefer explicit ApplyText when provided
|
||||
$stepName = $feature.ApplyText
|
||||
} elseif ($feature.Label) {
|
||||
|
||||
@@ -179,7 +179,7 @@ function Show-ApplyModal {
|
||||
$applyRebootPanel.Visibility = 'Visible'
|
||||
}
|
||||
else {
|
||||
$script:ApplyCompletionMessageEl.Text = "Your clean system is ready. Thanks for using Win11Debloat!"
|
||||
$script:ApplyCompletionMessageEl.Text = "Your system is ready. Thanks for using Win11Debloat!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1484,6 +1484,57 @@ function Show-MainWindow {
|
||||
UpdateNavigationButtons
|
||||
})
|
||||
|
||||
# Handle Home Revert link button
|
||||
$homeRevertLinkBtn = $window.FindName('HomeRevertLinkBtn')
|
||||
if ($homeRevertLinkBtn) {
|
||||
if (-not (Test-Path $script:SavedSettingsFilePath)) {
|
||||
$homeRevertLinkBtn.Visibility = 'Collapsed'
|
||||
}
|
||||
|
||||
$homeRevertLinkBtn.Add_Click({
|
||||
$savedSettings = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
|
||||
if (-not $savedSettings -or -not $savedSettings.Settings) {
|
||||
return
|
||||
}
|
||||
|
||||
$revertSelection = Show-RevertSettingsModal -Owner $window -LastUsedSettings $savedSettings
|
||||
$selectedFeatureIds = @($revertSelection.SelectedFeatureIds)
|
||||
$shouldRestartExplorer = ($revertSelection.RestartExplorer -eq $true)
|
||||
|
||||
if (-not $selectedFeatureIds -or $selectedFeatureIds.Count -eq 0) {
|
||||
return
|
||||
}
|
||||
|
||||
# Keep runtime/control parameters and clear actionable selections before adding selected revert targets.
|
||||
$actionableKeys = @()
|
||||
foreach ($k in $script:Params.Keys) {
|
||||
if ($script:ControlParams -notcontains $k) {
|
||||
$actionableKeys += $k
|
||||
}
|
||||
}
|
||||
foreach ($k in $actionableKeys) {
|
||||
$script:Params.Remove($k)
|
||||
}
|
||||
|
||||
AddParameter 'Undo'
|
||||
|
||||
foreach ($featureId in $selectedFeatureIds) {
|
||||
if ($script:Features.ContainsKey($featureId)) {
|
||||
$feature = $script:Features[$featureId]
|
||||
if ($feature.RegistryUndoKey -and $feature.UndoAction) {
|
||||
AddParameter $featureId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Use the existing apply modal to execute and present progress/completion.
|
||||
Show-ApplyModal -Owner $window -RestartExplorer $shouldRestartExplorer
|
||||
|
||||
# Close the main window after the apply dialog closes
|
||||
$window.Close()
|
||||
})
|
||||
}
|
||||
|
||||
# Handle Home Default Mode button - apply defaults and navigate directly to overview
|
||||
$homeDefaultModeBtn = $window.FindName('HomeDefaultModeBtn')
|
||||
$homeDefaultModeBtn.Add_Click({
|
||||
|
||||
225
Scripts/GUI/Show-RevertSettingsModal.ps1
Normal file
225
Scripts/GUI/Show-RevertSettingsModal.ps1
Normal file
@@ -0,0 +1,225 @@
|
||||
function Show-RevertSettingsModal {
|
||||
param (
|
||||
[Parameter(Mandatory=$false)]
|
||||
[System.Windows.Window]$Owner = $null,
|
||||
[Parameter(Mandatory=$true)]
|
||||
$LastUsedSettings
|
||||
)
|
||||
|
||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
|
||||
|
||||
$usesDarkMode = GetSystemUsesDarkMode
|
||||
|
||||
# Determine owner window
|
||||
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
|
||||
|
||||
# Show overlay if owner window exists
|
||||
$overlay = $null
|
||||
$overlayWasAlreadyVisible = $false
|
||||
if ($ownerWindow) {
|
||||
try {
|
||||
$overlay = $ownerWindow.FindName('ModalOverlay')
|
||||
if ($overlay) {
|
||||
$overlayWasAlreadyVisible = ($overlay.Visibility -eq 'Visible')
|
||||
if (-not $overlayWasAlreadyVisible) {
|
||||
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Visible' })
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:RevertSettingsWindowSchema -Raw
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$revertWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
}
|
||||
finally {
|
||||
$reader.Close()
|
||||
}
|
||||
|
||||
if ($ownerWindow) {
|
||||
try {
|
||||
$revertWindow.Owner = $ownerWindow
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
SetWindowThemeResources -window $revertWindow -usesDarkMode $usesDarkMode
|
||||
|
||||
$itemsPanel = $revertWindow.FindName('RevertItemsPanel')
|
||||
$countText = $revertWindow.FindName('RevertSelectionCount')
|
||||
$selectAllBtn = $revertWindow.FindName('RevertSelectAllBtn')
|
||||
$clearBtn = $revertWindow.FindName('RevertClearBtn')
|
||||
$applyBtn = $revertWindow.FindName('RevertApplyBtn')
|
||||
$cancelBtn = $revertWindow.FindName('RevertCancelBtn')
|
||||
$restartExplorerCheckbox = $revertWindow.FindName('RevertRestartExplorerCheckBox')
|
||||
|
||||
$entryCheckboxes = @()
|
||||
$featureCheckboxStyle = $null
|
||||
if ($ownerWindow) {
|
||||
try {
|
||||
$featureCheckboxStyle = $ownerWindow.FindResource('FeatureCheckboxStyle')
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if ($restartExplorerCheckbox -and $featureCheckboxStyle) {
|
||||
$restartExplorerCheckbox.Style = $featureCheckboxStyle
|
||||
}
|
||||
|
||||
foreach ($setting in $LastUsedSettings.Settings) {
|
||||
if ($setting.Value -ne $true) { continue }
|
||||
if ($setting.Name -eq 'Apps' -or $setting.Name -eq 'RemoveApps' -or $setting.Name -eq 'CreateRestorePoint') { continue }
|
||||
|
||||
$feature = $null
|
||||
if ($script:Features.ContainsKey($setting.Name)) {
|
||||
$feature = $script:Features[$setting.Name]
|
||||
}
|
||||
$undoFeature = GetUndoFeatureForParam -paramKey $setting.Name
|
||||
|
||||
$label = $setting.Name
|
||||
if ($feature -and $feature.Label) {
|
||||
if ($feature.Action) {
|
||||
$label = "$($feature.Action) $($feature.Label)"
|
||||
}
|
||||
else {
|
||||
$label = $feature.Label
|
||||
}
|
||||
}
|
||||
|
||||
$undoLabel = if ($undoFeature -and $undoFeature.Label) {
|
||||
"$($undoFeature.UndoAction) $($undoFeature.Label)"
|
||||
} else {
|
||||
'No revert action available'
|
||||
}
|
||||
|
||||
$canUndo = ($undoFeature -ne $null)
|
||||
|
||||
$itemBorder = New-Object System.Windows.Controls.Border
|
||||
$itemBorder.Style = $revertWindow.FindResource('RevertItemBorderStyle')
|
||||
|
||||
$row = New-Object System.Windows.Controls.StackPanel
|
||||
$row.Style = $revertWindow.FindResource('RevertItemRowStyle')
|
||||
|
||||
$checkbox = New-Object System.Windows.Controls.CheckBox
|
||||
$checkbox.Content = $label
|
||||
$checkbox.Tag = $setting.Name
|
||||
if ($featureCheckboxStyle) {
|
||||
$checkbox.Style = $featureCheckboxStyle
|
||||
}
|
||||
else {
|
||||
$checkbox.Foreground = $revertWindow.FindResource('FgColor')
|
||||
}
|
||||
$checkbox.Margin = [System.Windows.Thickness]::new(0, 0, 0, 3)
|
||||
$checkbox.IsEnabled = $canUndo
|
||||
|
||||
$undoText = New-Object System.Windows.Controls.TextBlock
|
||||
$undoText.Text = if ($canUndo) { "Revert to: $undoLabel" } else { 'Revert not supported for this setting' }
|
||||
$undoText.Style = $revertWindow.FindResource('RevertItemUndoTextStyle')
|
||||
$undoText.Opacity = if ($canUndo) { 0.75 } else { 0.4 }
|
||||
|
||||
$row.Children.Add($checkbox) | Out-Null
|
||||
$row.Children.Add($undoText) | Out-Null
|
||||
$itemBorder.Child = $row
|
||||
|
||||
$itemsPanel.Children.Add($itemBorder) | Out-Null
|
||||
$entryCheckboxes += $checkbox
|
||||
}
|
||||
|
||||
# Remove the divider from the last entry for cleaner list termination.
|
||||
if ($itemsPanel.Children.Count -gt 0) {
|
||||
$lastItem = $itemsPanel.Children[$itemsPanel.Children.Count - 1]
|
||||
if ($lastItem -is [System.Windows.Controls.Border]) {
|
||||
$lastItem.BorderThickness = [System.Windows.Thickness]::new(0)
|
||||
}
|
||||
}
|
||||
|
||||
if ($entryCheckboxes.Count -eq 0) {
|
||||
$emptyText = New-Object System.Windows.Controls.TextBlock
|
||||
$emptyText.Text = 'No previously applied tweaks can be reverted'
|
||||
$emptyText.Style = $revertWindow.FindResource('RevertEmptyTextStyle')
|
||||
$itemsPanel.Children.Add($emptyText) | Out-Null
|
||||
$selectAllBtn.IsEnabled = $false
|
||||
$clearBtn.IsEnabled = $false
|
||||
}
|
||||
|
||||
$updateState = {
|
||||
$selectedCount = 0
|
||||
foreach ($cb in $entryCheckboxes) {
|
||||
if ($cb.IsEnabled -and $cb.IsChecked -eq $true) {
|
||||
$selectedCount++
|
||||
}
|
||||
}
|
||||
|
||||
$countText.Text = "$selectedCount settings selected"
|
||||
$applyBtn.IsEnabled = ($selectedCount -gt 0)
|
||||
}
|
||||
|
||||
foreach ($cb in $entryCheckboxes) {
|
||||
$cb.Add_Checked($updateState)
|
||||
$cb.Add_Unchecked($updateState)
|
||||
}
|
||||
|
||||
$selectAllBtn.Add_Click({
|
||||
foreach ($cb in $entryCheckboxes) {
|
||||
if ($cb.IsEnabled) {
|
||||
$cb.IsChecked = $true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$clearBtn.Add_Click({
|
||||
foreach ($cb in $entryCheckboxes) {
|
||||
if ($cb.IsEnabled) {
|
||||
$cb.IsChecked = $false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$cancelHandler = {
|
||||
$revertWindow.Tag = [PSCustomObject]@{
|
||||
SelectedFeatureIds = @()
|
||||
RestartExplorer = ($restartExplorerCheckbox -and $restartExplorerCheckbox.IsChecked -eq $true)
|
||||
}
|
||||
$revertWindow.Close()
|
||||
}
|
||||
|
||||
$cancelBtn.Add_Click($cancelHandler)
|
||||
|
||||
$applyBtn.Add_Click({
|
||||
$selected = @()
|
||||
foreach ($cb in $entryCheckboxes) {
|
||||
if ($cb.IsEnabled -and $cb.IsChecked -eq $true -and $cb.Tag) {
|
||||
$selected += $cb.Tag
|
||||
}
|
||||
}
|
||||
|
||||
$revertWindow.Tag = [PSCustomObject]@{
|
||||
SelectedFeatureIds = $selected
|
||||
RestartExplorer = ($restartExplorerCheckbox -and $restartExplorerCheckbox.IsChecked -eq $true)
|
||||
}
|
||||
$revertWindow.Close()
|
||||
})
|
||||
|
||||
& $updateState
|
||||
|
||||
$revertWindow.ShowDialog() | Out-Null
|
||||
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
try {
|
||||
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if ($revertWindow.Tag) {
|
||||
return $revertWindow.Tag
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
SelectedFeatureIds = @()
|
||||
RestartExplorer = $true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
param (
|
||||
[switch]$CLI,
|
||||
[switch]$Silent,
|
||||
[switch]$Undo,
|
||||
[switch]$Verbose,
|
||||
[switch]$Sysprep,
|
||||
[string]$LogPath,
|
||||
|
||||
17
Scripts/Helpers/GetUndoFeatureForParam.ps1
Normal file
17
Scripts/Helpers/GetUndoFeatureForParam.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
# Returns the feature metadata for a parameter when it supports undo; otherwise returns $null.
|
||||
function GetUndoFeatureForParam {
|
||||
param (
|
||||
[string]$paramKey
|
||||
)
|
||||
|
||||
if (-not $script:Features -or -not $script:Features.ContainsKey($paramKey)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$feature = $script:Features[$paramKey]
|
||||
if (-not ($feature.RegistryUndoKey -and $feature.UndoAction)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $feature
|
||||
}
|
||||
Reference in New Issue
Block a user