Add comprehensive test suite, fix minor issues, rename function and file names to match approved verbs (#708)

This commit is contained in:
Jeffrey
2026-07-19 22:06:07 +02:00
committed by GitHub
parent a7292e4f35
commit 9c033dbf98
116 changed files with 4629 additions and 650 deletions
@@ -1,6 +1,20 @@
# Applies settings from a JSON object to UI controls (checkboxes and comboboxes)
# Used by LoadDefaultsBtn and LoadLastUsedBtn in the UI
function ApplySettingsToUiControls {
<#
.SYNOPSIS
Applies enabled settings from JSON to mapped checkbox and combo-box controls.
.PARAMETER Window
The window that owns the mapped controls.
.PARAMETER SettingsJson
The settings object containing a Settings collection.
.PARAMETER UiControlMappings
The feature-to-control mapping used to locate and update controls.
.OUTPUTS
System.Boolean. $false for invalid settings input; otherwise $true.
#>
function Apply-SettingsToUiControls {
param (
$window,
$settingsJson,
@@ -1,10 +1,20 @@
# Attaches shift-click selection behavior to a checkbox in an apps panel
# Parameters:
# - $checkbox: The checkbox to attach the behavior to
# - $appsPanel: The StackPanel containing checkbox items
# - $lastSelectedCheckboxRef: A reference to a variable storing the last clicked checkbox
# - $updateStatusCallback: Optional callback to update selection status
function AttachShiftClickBehavior {
<#
.SYNOPSIS
Attaches shift-click range-selection behavior to an application checkbox.
.PARAMETER Checkbox
The checkbox that receives the mouse event handler.
.PARAMETER AppsPanel
The panel whose visible checkboxes participate in range selection.
.PARAMETER LastSelectedCheckboxRef
A reference that stores the previously clicked checkbox.
.PARAMETER UpdateStatusCallback
An optional callback invoked after a range selection changes.
#>
function Attach-ShiftClickBehavior {
param (
[System.Windows.Controls.CheckBox]$checkbox,
[System.Windows.Controls.StackPanel]$appsPanel,
@@ -1,5 +1,11 @@
# Checks if the system is set to use dark mode for apps
function GetSystemUsesDarkMode {
<#
.SYNOPSIS
Returns whether Windows apps are configured to use dark mode.
.OUTPUTS
System.Boolean. $false when the personalization setting cannot be read.
#>
function Get-SystemUsesDarkMode {
try {
$personalizeKey = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'
+53 -12
View File
@@ -223,7 +223,7 @@ function Update-AppPresetStates {
$script:UpdatingPresets = $true
try {
# Helper: count matching and checked apps, set checkbox state
function SetPresetState($CheckBox, [scriptblock]$MatchFilter) {
function Set-PresetState($CheckBox, [scriptblock]$MatchFilter) {
$total = 0; $checked = 0
foreach ($child in $AppsPanel.Children) {
if ($child -is [System.Windows.Controls.CheckBox]) {
@@ -241,15 +241,15 @@ function Update-AppPresetStates {
$presetDefaultApps = $window.FindName('PresetDefaultApps')
$presetLastUsed = $window.FindName('PresetLastUsed')
SetPresetState $presetDefaultApps { param($c) $c.SelectedByDefault -eq $true }
Set-PresetState $presetDefaultApps { param($c) $c.SelectedByDefault -eq $true }
foreach ($jsonCb in $script:JsonPresetCheckboxes) {
$localIds = $jsonCb.PresetAppIds
SetPresetState $jsonCb { param($c) (@($c.AppIds) | Where-Object { $localIds -contains $_ }).Count -gt 0 }.GetNewClosure()
Set-PresetState $jsonCb { param($c) (@($c.AppIds) | Where-Object { $localIds -contains $_ }).Count -gt 0 }.GetNewClosure()
}
# Last used preset: only update if it's visible (has saved apps)
if ($presetLastUsed.Visibility -ne 'Collapsed' -and $script:SavedAppIds) {
SetPresetState $presetLastUsed { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 }
Set-PresetState $presetLastUsed { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 }
}
}
finally {
@@ -304,7 +304,29 @@ function Find-ParentScrollViewer {
return $null
}
function Load-AppsWithList {
<#
.SYNOPSIS
Loads application details and adds their interactive checkboxes to the main window.
.PARAMETER Window
The main application window and resource owner.
.PARAMETER AppsPanel
The panel populated with application checkboxes.
.PARAMETER OnlyInstalledAppsBox
The filter control that determines whether only installed apps are loaded.
.PARAMETER LoadingAppsIndicator
The loading indicator shown while application details are prepared.
.PARAMETER ImportConfigBtn
The optional import control re-enabled after loading completes.
.PARAMETER ListOfApps
An optional pre-fetched list of installed WinGet applications.
#>
function Add-AppsToMainWindow {
param(
[System.Windows.Window]$Window,
[System.Windows.Controls.Panel]$AppsPanel,
@@ -335,7 +357,7 @@ function Load-AppsWithList {
$script:AppsListFilePath = $appsListFilePath
. $helperScript
. $loaderScript
LoadAppsDetailsFromJson -OnlyInstalled:$onlyInstalled -InstalledList $installedList -InitialCheckedFromJson:$false
Import-AppDetailsFromJson -OnlyInstalled:$onlyInstalled -InstalledList $installedList -InitialCheckedFromJson:$false
} -ArgumentList $loaderScriptPath, $helperScriptPath, $appsFilePath, $ListOfApps, $onlyInstalled
}
@@ -435,7 +457,7 @@ function Load-AppsWithList {
-AppRemovalScopeDescription $w.FindName('AppRemovalScopeDescription') `
-UserSelectionCombo $w.FindName('UserSelectionCombo')
})
AttachShiftClickBehavior -checkbox $checkbox -appsPanel $AppsPanel `
Attach-ShiftClickBehavior -checkbox $checkbox -appsPanel $AppsPanel `
-lastSelectedCheckboxRef ([ref]$script:MainWindowLastSelectedCheckbox) `
-updateStatusCallback {
$w = $script:MainWindow
@@ -449,7 +471,7 @@ function Load-AppsWithList {
$AppsPanel.Children.Add($checkbox) | Out-Null
if (($i + 1) % $batchSize -eq 0) { DoEvents }
if (($i + 1) % $batchSize -eq 0) { Invoke-DoEvents }
}
$sortArrowName = $Window.FindName('SortArrowName')
@@ -480,7 +502,26 @@ function Load-AppsWithList {
}
}
function Load-AppsIntoMainUI {
<#
.SYNOPSIS
Starts asynchronous loading of application checkboxes for the main window.
.PARAMETER Window
The main application window.
.PARAMETER AppsPanel
The panel that receives application checkboxes.
.PARAMETER OnlyInstalledAppsBox
The installed-applications filter control.
.PARAMETER LoadingAppsIndicator
The loading indicator shown until loading completes.
.PARAMETER ImportConfigBtn
The optional import control disabled while loading is in progress.
#>
function Initialize-MainWindowApps {
param(
[System.Windows.Window]$Window,
[System.Windows.Controls.Panel]$AppsPanel,
@@ -511,7 +552,7 @@ function Load-AppsIntoMainUI {
# Force a render so the loading indicator is visible, then schedule the
# actual loading at Background priority so this call returns immediately.
# This is critical when called from Add_Loaded: the window must finish
# its initialization before we start a nested message pump via DoEvents.
# its initialization before we start a nested message pump via Invoke-DoEvents.
$Window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Render, [action] {})
$Window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action] {
try {
@@ -519,7 +560,7 @@ function Load-AppsIntoMainUI {
if ($OnlyInstalledAppsBox.IsChecked -and ($script:WingetInstalled -eq $true)) {
Write-Host "Retrieving installed apps via winget..."
$listOfApps = GetInstalledAppsViaWinget -TimeOut 20 -NonBlocking
$listOfApps = Get-WingetInstalledApps -TimeOut 20 -NonBlocking
if ($null -eq $listOfApps) {
Write-Warning "WinGet returned no data (command timed out or failed)"
@@ -528,7 +569,7 @@ function Load-AppsIntoMainUI {
}
}
Load-AppsWithList -Window $Window -AppsPanel $AppsPanel -OnlyInstalledAppsBox $OnlyInstalledAppsBox `
Add-AppsToMainWindow -Window $Window -AppsPanel $AppsPanel -OnlyInstalledAppsBox $OnlyInstalledAppsBox `
-LoadingAppsIndicator $LoadingAppsIndicator -ImportConfigBtn $ImportConfigBtn -ListOfApps $listOfApps
}
catch {
+34 -8
View File
@@ -147,7 +147,20 @@ function Invoke-ShowChangesOverview {
Show-MessageBox -Message $message -Title 'Selected Changes' -Button 'OK' -Icon 'None' -Width 600
}
function Build-TweakPresetControlMap {
<#
.SYNOPSIS
Builds the control values needed to apply a saved tweak preset.
.PARAMETER Window
The window that owns the visible tweak controls.
.PARAMETER SettingsJson
The saved settings object to translate into control values.
.OUTPUTS
System.Collections.Hashtable. Control metadata keyed by control name.
#>
function Get-TweakPresetControlMap {
param(
[System.Windows.Window]$Window,
$SettingsJson
@@ -158,7 +171,7 @@ function Build-TweakPresetControlMap {
return $presetMap
}
# FeatureId -> control metadata, similar to ApplySettingsToUiControls lookup.
# FeatureId -> control metadata, similar to Apply-SettingsToUiControls lookup.
$featureIdIndex = @{}
foreach ($controlName in $script:UiControlMappings.Keys) {
$control = $Window.FindName($controlName)
@@ -199,7 +212,20 @@ function Build-TweakPresetControlMap {
return $presetMap
}
function Build-CategoryTweakPresetMap {
<#
.SYNOPSIS
Builds the enabled state map for visible tweak controls in a category.
.PARAMETER Window
The window that owns the visible tweak controls.
.PARAMETER Category
The category whose mapped controls are included.
.OUTPUTS
System.Collections.Hashtable. Control metadata keyed by control name.
#>
function Get-CategoryTweakPresetMap {
param(
[System.Windows.Window]$Window,
[string]$Category
@@ -375,10 +401,10 @@ function Initialize-TweakPresetSources {
$LastUsedSettingsJson
)
$script:DefaultTweakPresetMap = Build-TweakPresetControlMap -Window $Window -SettingsJson $DefaultSettingsJson
$script:LastUsedTweakPresetMap = Build-TweakPresetControlMap -Window $Window -SettingsJson $LastUsedSettingsJson
$script:PrivacyTweakPresetMap = Build-CategoryTweakPresetMap -Window $Window -Category 'Privacy & Suggested Content'
$script:AITweakPresetMap = Build-CategoryTweakPresetMap -Window $Window -Category 'AI'
$script:DefaultTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $DefaultSettingsJson
$script:LastUsedTweakPresetMap = Get-TweakPresetControlMap -Window $Window -SettingsJson $LastUsedSettingsJson
$script:PrivacyTweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -Category 'Privacy & Suggested Content'
$script:AITweakPresetMap = Get-CategoryTweakPresetMap -Window $Window -Category 'AI'
$presetLastUsedTweaksBtn = $Window.FindName('PresetLastUsedTweaksBtn')
if ($presetLastUsedTweaksBtn) {
@@ -421,7 +447,7 @@ function Update-UserSelectionDescription {
switch ($UserSelectionCombo.SelectedIndex) {
0 {
$currentUserName = GetUserName
$currentUserName = Get-UserName
if ([string]::IsNullOrWhiteSpace($currentUserName)) {
$UserSelectionDescription.Text = "The currently logged-in user profile"
}
+73 -14
View File
@@ -1,13 +1,26 @@
# MainWindow-TweaksBuilder.ps1
# Dynamic tweaks UI construction from Features.json, tweak state management, selection clear, and search/highlight.
function Build-DynamicTweaks {
<#
.SYNOPSIS
Builds the main window's dynamic tweak controls from Features.json.
.PARAMETER Window
The main window whose category columns receive the generated controls.
.PARAMETER WinVersion
The Windows build number used for the category-icon fallback.
.NOTES
Initializes script-scoped control and category mappings used by the tweak UI.
#>
function New-DynamicTweakControls {
param(
[System.Windows.Window]$Window,
[int]$WinVersion
)
$featuresJson = LoadJsonFile -filePath $script:FeaturesFilePath -expectedVersion "1.0"
$featuresJson = Import-JsonFile -filePath $script:FeaturesFilePath -expectedVersion "1.0"
if (-not $featuresJson) {
throw "Unable to load Features.json file. The GUI cannot continue without feature definitions."
@@ -29,7 +42,26 @@ function Build-DynamicTweaks {
$script:TweaksCompactMode = $null
$script:TweaksCardsMovedFromCol2 = @()
function CreateLabeledCombo($parent, $labelText, $comboName, $items) {
<#
.SYNOPSIS
Creates and registers a labeled combo box or a checkbox for a tweak.
.PARAMETER Parent
The panel that receives the generated control.
.PARAMETER LabelText
The display and automation label for the tweak.
.PARAMETER ComboName
The name used to register the generated control.
.PARAMETER Items
The available tweak options; two options produce a checkbox.
.OUTPUTS
System.Windows.Controls.Control. The generated checkbox or combo box.
#>
function New-LabeledCombo($parent, $labelText, $comboName, $items) {
# If only 2 items (No Change + one option), use a checkbox instead
if ($items.Count -eq 2) {
$checkbox = New-Object System.Windows.Controls.CheckBox
@@ -96,7 +128,17 @@ function Build-DynamicTweaks {
return $combo
}
function GetWikiUrlForCategory($category) {
<#
.SYNOPSIS
Returns the Features wiki URL for a tweak category.
.PARAMETER Category
The category name converted to a wiki anchor.
.OUTPUTS
System.String. The category URL, or the Features page for an empty category.
#>
function Get-WikiUrlForCategory($category) {
if (-not $category) { return 'https://github.com/Raphire/Win11Debloat/wiki/Features' }
$slug = $category.ToLowerInvariant()
@@ -107,7 +149,17 @@ function Build-DynamicTweaks {
return "https://github.com/Raphire/Win11Debloat/wiki/Features#$slug"
}
function GetOrCreateCategoryCard($categoryObj) {
<#
.SYNOPSIS
Returns the existing category panel or creates and registers a new one.
.PARAMETER CategoryObj
The category definition containing Name and Icon properties.
.OUTPUTS
System.Windows.Controls.StackPanel. The category's content panel.
#>
function Get-OrCreateCategoryCard($categoryObj) {
$categoryName = $categoryObj.Name
$categoryIcon = $categoryObj.Icon
@@ -152,7 +204,7 @@ function Build-DynamicTweaks {
$helpBtn = New-Object System.Windows.Controls.Button
$helpBtn.Content = $helpIcon
$helpBtn.ToolTip = "Open the wiki for more info on '$categoryName' tweaks"
$helpBtn.Tag = (GetWikiUrlForCategory -category $categoryName)
$helpBtn.Tag = (Get-WikiUrlForCategory -category $categoryName)
$helpBtn.Style = $Window.Resources['CategoryHelpLinkButtonStyle']
$helpBtn.Add_Click({
param($button, $e)
@@ -289,8 +341,8 @@ function Build-DynamicTweaks {
if ($soleFeature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($soleFeature.FeatureId -match '^Enable') { $opt = 'Enable' }
$items = @('No Change', $opt)
$comboName = ("Feature_{0}_Combo" -f $soleFeature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
if (-not $panel) { $panel = GetOrCreateCategoryCard -categoryObj $categoryObj }
$combo = CreateLabeledCombo -parent $panel -labelText $soleFeature.Label -comboName $comboName -items $items
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
$combo = New-LabeledCombo -parent $panel -labelText $soleFeature.Label -comboName $comboName -items $items
# attach tooltip from Features.json if present
if ($soleFeature.ToolTip -or $soleFeature.DisableWhenApplied -eq $true) {
$tooltipText = $soleFeature.ToolTip
@@ -314,8 +366,8 @@ function Build-DynamicTweaks {
$items = @('No Change') + ($filteredValues | ForEach-Object { $_.Label })
$comboName = 'Group_{0}Combo' -f $group.GroupId
if (-not $panel) { $panel = GetOrCreateCategoryCard -categoryObj $categoryObj }
$combo = CreateLabeledCombo -parent $panel -labelText $group.Label -comboName $comboName -items $items
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
$combo = New-LabeledCombo -parent $panel -labelText $group.Label -comboName $comboName -items $items
# attach tooltip from UiGroups if present
if ($group.ToolTip) {
$tipBlock = New-Object System.Windows.Controls.TextBlock
@@ -335,8 +387,8 @@ function Build-DynamicTweaks {
if ($feature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($feature.FeatureId -match '^Enable') { $opt = 'Enable' }
$items = @('No Change', $opt)
$comboName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
if (-not $panel) { $panel = GetOrCreateCategoryCard -categoryObj $categoryObj }
$combo = CreateLabeledCombo -parent $panel -labelText $feature.Label -comboName $comboName -items $items
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
$combo = New-LabeledCombo -parent $panel -labelText $feature.Label -comboName $comboName -items $items
# attach tooltip from Features.json if present, and include the disabled-state reason
if ($feature.ToolTip -or $feature.DisableWhenApplied -eq $true) {
$tooltipText = $feature.ToolTip
@@ -377,7 +429,7 @@ function Update-CurrentTweakSystemState {
if (-not $script:UiControlMappings) { return }
if (-not $script:Features) { return }
$featuresJson = LoadJsonFile -filePath $script:FeaturesFilePath -expectedVersion "1.0"
$featuresJson = Import-JsonFile -filePath $script:FeaturesFilePath -expectedVersion "1.0"
if (-not $featuresJson) { return }
$groupMap = @{}
@@ -423,7 +475,14 @@ function Update-CurrentTweakSystemState {
}
}
function Load-CurrentTweakStateIntoUI {
<#
.SYNOPSIS
Updates tweak controls to reflect the current system state.
.PARAMETER Window
The window that owns the generated tweak controls.
#>
function Set-CurrentTweakStateInUi {
param([System.Windows.Window]$Window)
Update-CurrentTweakSystemState -Window $Window -ApplyToUi:$true
@@ -17,13 +17,13 @@
When $true, dark theme colors are applied; when $false, light theme colors.
.EXAMPLE
SetWindowThemeResources -window $MainWindow -usesDarkMode $true
Set-WindowThemeResources -window $MainWindow -usesDarkMode $true
.EXAMPLE
SetWindowThemeResources -window $Dialog -usesDarkMode $false
Set-WindowThemeResources -window $Dialog -usesDarkMode $false
#>
# Sets resource colors for a WPF window based on dark mode preference
function SetWindowThemeResources {
function Set-WindowThemeResources {
param (
$window,
[bool]$usesDarkMode
+10 -3
View File
@@ -1,3 +1,10 @@
<#
.SYNOPSIS
Displays the themed About dialog for the application.
.PARAMETER Owner
The optional window that owns the dialog and its modal overlay.
#>
function Show-AboutDialog {
param (
[Parameter(Mandatory=$false)]
@@ -6,7 +13,7 @@ function Show-AboutDialog {
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
$usesDarkMode = Get-SystemUsesDarkMode
# Determine owner window
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
@@ -42,7 +49,7 @@ function Show-AboutDialog {
}
# Apply theme resources
SetWindowThemeResources -window $aboutWindow -usesDarkMode $usesDarkMode
Set-WindowThemeResources -window $aboutWindow -usesDarkMode $usesDarkMode
# Get UI elements
$titleBar = $aboutWindow.FindName('TitleBar')
@@ -95,4 +102,4 @@ function Show-AboutDialog {
catch { }
}
}
}
}
+24 -12
View File
@@ -1,8 +1,14 @@
# Shows application selection window that allows the user to select what apps they want to remove or keep
<#
.SYNOPSIS
Displays the application-selection dialog and records the confirmed selections.
.OUTPUTS
System.Nullable[System.Boolean]. The dialog result; confirmed application IDs are stored in $script:SelectedApps.
#>
function Show-AppSelectionWindow {
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
$usesDarkMode = Get-SystemUsesDarkMode
# Show overlay if main window exists
$overlay = $null
@@ -34,7 +40,7 @@ function Show-AppSelectionWindow {
catch { }
}
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
$appsPanel = $window.FindName('AppsPanel')
$checkAllBox = $window.FindName('CheckAllBox')
@@ -46,8 +52,14 @@ function Show-AppSelectionWindow {
# Track the last selected checkbox for shift-click range selection
$script:AppSelectionWindowLastSelectedCheckbox = $null
# Loads apps into the apps UI
function LoadApps {
<#
.SYNOPSIS
Reloads the application-selection checkboxes using the current installed-apps filter.
.NOTES
Updates the dialog loading indicator and resets range-selection state.
#>
function Load-Apps {
# Show loading indicator
$loadingIndicator.Visibility = 'Visible'
$window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{})
@@ -57,7 +69,7 @@ function Show-AppSelectionWindow {
if ($onlyInstalledBox.IsChecked -and ($script:WingetInstalled -eq $true)) {
# Attempt to get a list of installed apps via WinGet, times out after 10 seconds
$listOfApps = GetInstalledAppsViaWinget -TimeOut 10 -NonBlocking
$listOfApps = Get-WingetInstalledApps -TimeOut 10 -NonBlocking
if ($null -eq $listOfApps) {
# Show error that the script was unable to get list of apps from WinGet
Show-MessageBox -Message 'Unable to load list of installed apps via WinGet.' -Title 'Error' -Button 'OK' -Icon 'Error' -Owner $window | Out-Null
@@ -65,7 +77,7 @@ function Show-AppSelectionWindow {
}
}
$appsToAdd = LoadAppsDetailsFromJson -OnlyInstalled:$onlyInstalledBox.IsChecked -InstalledList $listOfApps -InitialCheckedFromJson:$true
$appsToAdd = Import-AppDetailsFromJson -OnlyInstalled:$onlyInstalledBox.IsChecked -InstalledList $listOfApps -InitialCheckedFromJson:$true
# Reset the last selected checkbox when loading a new list
$script:AppSelectionWindowLastSelectedCheckbox = $null
@@ -82,7 +94,7 @@ function Show-AppSelectionWindow {
$checkbox.Style = $window.Resources["AppsPanelCheckBoxStyle"]
# Attach shift-click behavior for range selection
AttachShiftClickBehavior -checkbox $checkbox -appsPanel $appsPanel -lastSelectedCheckboxRef ([ref]$script:AppSelectionWindowLastSelectedCheckbox)
Attach-ShiftClickBehavior -checkbox $checkbox -appsPanel $appsPanel -lastSelectedCheckboxRef ([ref]$script:AppSelectionWindowLastSelectedCheckbox)
$appsPanel.Children.Add($checkbox) | Out-Null
}
@@ -112,8 +124,8 @@ function Show-AppSelectionWindow {
}
})
$onlyInstalledBox.Add_Checked({ LoadApps })
$onlyInstalledBox.Add_Unchecked({ LoadApps })
$onlyInstalledBox.Add_Checked({ Load-Apps })
$onlyInstalledBox.Add_Unchecked({ Load-Apps })
$confirmBtn.Add_Click({
$selectedApps = @()
@@ -130,7 +142,7 @@ function Show-AppSelectionWindow {
return
}
if (-not (ConfirmUnsafeAppRemoval -SelectedApps $selectedApps -Owner $window)) {
if (-not (Confirm-UnsafeAppRemoval -SelectedApps $selectedApps -Owner $window)) {
return
}
@@ -141,7 +153,7 @@ function Show-AppSelectionWindow {
# Load apps after window is shown (allows UI to render first)
$window.Add_ContentRendered({
$window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ LoadApps }) | Out-Null
$window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Load-Apps }) | Out-Null
})
# Show the window and return dialog result
+18 -8
View File
@@ -1,14 +1,24 @@
<#
.SYNOPSIS
Displays the modal progress window while selected changes are applied.
.PARAMETER Owner
The optional window that owns the modal and its overlay.
.PARAMETER InvokeRestartExplorer
Indicates whether the modal should run the Explorer-restart flow after applying changes.
#>
function Show-ApplyModal {
param (
[Parameter(Mandatory=$false)]
[System.Windows.Window]$Owner = $null,
[Parameter(Mandatory=$false)]
[bool]$RestartExplorer = $false
[bool]$InvokeRestartExplorer = $false
)
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
$usesDarkMode = Get-SystemUsesDarkMode
# Determine owner window
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
@@ -44,7 +54,7 @@ function Show-ApplyModal {
}
# Apply theme resources
SetWindowThemeResources -window $applyWindow -usesDarkMode $usesDarkMode
Set-WindowThemeResources -window $applyWindow -usesDarkMode $usesDarkMode
# Get UI elements
$script:ApplyInProgressPanel = $applyWindow.FindName('ApplyInProgressPanel')
@@ -81,7 +91,7 @@ function Show-ApplyModal {
$pct = if ($totalSteps -gt 0) { [math]::Round((($currentStep - 1) / $totalSteps) * 100) } else { 0 }
$script:ApplyProgressBarEl.Value = $pct
# Process pending window messages to keep UI responsive
DoEvents
Invoke-DoEvents
}
# Sub-step callback updates step name and interpolates progress bar within the current step
@@ -96,7 +106,7 @@ function Show-ApplyModal {
$stepFraction = ($subIndex / $subCount) / $totalSteps
$script:ApplyProgressBarEl.Value = [math]::Round(($baseProgress + $stepFraction) * 100)
}
DoEvents
Invoke-DoEvents
}
# Run changes in background to keep UI responsive
@@ -107,8 +117,8 @@ function Show-ApplyModal {
$registryImportFailureCount = [int]$script:RegistryImportFailures
# Restart explorer if requested
if ($RestartExplorer -and -not $script:CancelRequested) {
RestartExplorer
if ($InvokeRestartExplorer -and -not $script:CancelRequested) {
Invoke-RestartExplorer
# Wait for Explorer to finish relaunching, then reclaim focus.
Start-Sleep -Milliseconds 800
@@ -143,7 +153,7 @@ function Show-ApplyModal {
$script:ApplyCompletionTitleEl.Text = "Changes Applied"
# Show completion message with reboot instructions if any applied features require reboot
if ($RestartExplorer) {
if ($InvokeRestartExplorer) {
$rebootFeatures = Get-RebootFeatureLabels
if ($rebootFeatures.Count -gt 0) {
@@ -1,3 +1,7 @@
<#
.SYNOPSIS
Shows a modal category-selection dialog for importing or exporting configuration.
#>
function Show-ImportExportConfigWindow {
param (
[System.Windows.Window]$Owner,
@@ -45,7 +49,7 @@ function Show-ImportExportConfigWindow {
}
$dlg.Owner = $Owner
SetWindowThemeResources -window $dlg -usesDarkMode $UsesDarkMode
Set-WindowThemeResources -window $dlg -usesDarkMode $UsesDarkMode
# Copy the CheckBox default style from the main window so checkboxes get the themed template
try {
@@ -308,7 +312,11 @@ function Build-CategoryDetails {
return $details
}
function Apply-ImportedApplications {
<#
.SYNOPSIS
Applies imported application selections to the application checkboxes.
#>
function Set-ImportedApplications {
param (
[System.Windows.Controls.Panel]$AppsPanel,
[string[]]$AppIds
@@ -321,7 +329,11 @@ function Apply-ImportedApplications {
}
}
function Apply-ImportedTweakSettings {
<#
.SYNOPSIS
Applies imported tweak settings to their mapped UI controls.
#>
function Set-ImportedTweakSettings {
param (
[System.Windows.Window]$Owner,
[hashtable]$UiControlMappings,
@@ -329,10 +341,14 @@ function Apply-ImportedTweakSettings {
)
$settingsJson = [PSCustomObject]@{ Settings = @($TweakSettings) }
ApplySettingsToUiControls -window $Owner -settingsJson $settingsJson -uiControlMappings $UiControlMappings
Apply-SettingsToUiControls -window $Owner -settingsJson $settingsJson -uiControlMappings $UiControlMappings
}
function Apply-ImportedDeploymentSettings {
<#
.SYNOPSIS
Applies imported deployment settings to the deployment controls.
#>
function Set-ImportedDeploymentSettings {
param (
[System.Windows.Window]$Owner,
[System.Windows.Controls.ComboBox]$UserSelectionCombo,
@@ -368,6 +384,10 @@ function Apply-ImportedDeploymentSettings {
}
}
<#
.SYNOPSIS
Exports selected application, tweak, and deployment settings to a configuration file.
#>
function Export-Configuration {
param (
[System.Windows.Window]$Owner,
@@ -427,7 +447,7 @@ function Export-Configuration {
return
}
if (SaveToFile -Config $config -FilePath $saveDialog.FileName) {
if (Save-ToFile -Config $config -FilePath $saveDialog.FileName) {
Write-Host "Configuration exported successfully: $($saveDialog.FileName)"
Show-MessageBox -Message "Configuration exported successfully." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
}
@@ -437,6 +457,10 @@ function Export-Configuration {
}
}
<#
.SYNOPSIS
Imports selected application, tweak, and deployment settings from a configuration file.
#>
function Import-Configuration {
param (
[System.Windows.Window]$Owner,
@@ -462,7 +486,7 @@ function Import-Configuration {
Write-Host "Importing configuration from '$($openDialog.FileName)'..."
$config = LoadJsonFile -filePath $openDialog.FileName -expectedVersion '1.0'
$config = Import-JsonFile -filePath $openDialog.FileName -expectedVersion '1.0'
if (-not $config) {
Write-Error "Failed to read configuration file '$($openDialog.FileName)'"
Show-MessageBox -Message "Failed to read configuration file" -Title 'Invalid Config' -Button 'OK' -Icon 'Error' | Out-Null
@@ -504,7 +528,7 @@ function Import-Configuration {
)
Write-Host "Importing $($appIds.Count) app selection(s)."
Apply-ImportedApplications -AppsPanel $AppsPanel -AppIds $appIds
Set-ImportedApplications -AppsPanel $AppsPanel -AppIds $appIds
if ($OnAppsImported) {
& $OnAppsImported
@@ -513,11 +537,11 @@ function Import-Configuration {
if ($categories -contains 'System Tweaks' -and $config.Tweaks) {
$tweakCount = @($config.Tweaks).Count
Write-Host "Importing $tweakCount tweak(s)."
Apply-ImportedTweakSettings -Owner $Owner -UiControlMappings $UiControlMappings -TweakSettings @($config.Tweaks)
Set-ImportedTweakSettings -Owner $Owner -UiControlMappings $UiControlMappings -TweakSettings @($config.Tweaks)
}
if ($categories -contains 'Deployment Settings' -and $config.Deployment) {
Write-Host 'Importing deployment settings.'
Apply-ImportedDeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox -DeploymentSettings @($config.Deployment)
Set-ImportedDeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox -DeploymentSettings @($config.Deployment)
}
Write-Host 'Configuration imported successfully.'
+34 -30
View File
@@ -1,8 +1,12 @@
function Show-MainWindow {
<#
.SYNOPSIS
Creates and displays the main Win11Debloat window.
#>
function Show-MainWindow {
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase,System.Windows.Forms | Out-Null
$WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild
$usesDarkMode = GetSystemUsesDarkMode
$usesDarkMode = Get-SystemUsesDarkMode
# ---- Load XAML ----
$xaml = Get-Content -Path $script:MainWindowSchema -Raw
@@ -14,7 +18,7 @@
$reader.Close()
}
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
$mainBorder = $window.FindName('MainBorder')
$titleBarBackground = $window.FindName('TitleBarBackground')
@@ -223,7 +227,7 @@
if ($importConfigBtn) { $importConfigBtn.IsEnabled = $false }
# ---- Build JSON-defined app presets ----
foreach ($preset in (LoadAppPresetsFromJson)) {
foreach ($preset in (Import-AppPresetsFromJson)) {
$checkbox = New-Object System.Windows.Controls.CheckBox
$checkbox.Content = $preset.Name
$checkbox.IsThreeState = $true
@@ -301,8 +305,8 @@
# ---- Load apps ----
$appLoadStatusCallback = { Update-AppSelectionStatus -AppsPanel $appsPanel -AppSelectionStatus $appSelectionStatus -AppRemovalScopeCombo $appRemovalScopeCombo -AppRemovalScopeSection $appRemovalScopeSection -AppRemovalScopeDescription $appRemovalScopeDescription -UserSelectionCombo $userSelectionCombo }
$onlyInstalledAppsBox.Add_Checked({ Load-AppsIntoMainUI -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn })
$onlyInstalledAppsBox.Add_Unchecked({ Load-AppsIntoMainUI -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn })
$onlyInstalledAppsBox.Add_Checked({ Initialize-MainWindowApps -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn })
$onlyInstalledAppsBox.Add_Unchecked({ Initialize-MainWindowApps -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn })
# ---- App presets popup ----
$presetsPopup.Add_Opened({
@@ -617,9 +621,9 @@
$ShowCurrentlyAppliedTweaksCheckBox.IsChecked = $false
}
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
$defaultsJson = Import-JsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
if ($defaultsJson) {
ApplySettingsToUiControls -window $window -settingsJson $defaultsJson -uiControlMappings $script:UiControlMappings
Apply-SettingsToUiControls -window $window -settingsJson $defaultsJson -uiControlMappings $script:UiControlMappings
}
if ($script:IsLoadingApps) {
@@ -663,17 +667,17 @@
$hasAppSelection = ($selectedApps.Count -gt 0)
if ($selectedApps.Count -gt 0) {
if (-not (ConfirmUnsafeAppRemoval -SelectedApps $selectedApps -Owner $window)) { return }
if (-not (Confirm-UnsafeAppRemoval -SelectedApps $selectedApps -Owner $window)) { return }
AddParameter 'RemoveApps'
AddParameter 'Apps' ($selectedApps -join ',')
Add-Parameter 'RemoveApps'
Add-Parameter 'Apps' ($selectedApps -join ',')
$selectedScopeItem = $appRemovalScopeCombo.SelectedItem
if ($selectedScopeItem) {
switch ($selectedScopeItem.Content) {
"All users" { AddParameter 'AppRemovalTarget' 'AllUsers' }
"Current user only" { AddParameter 'AppRemovalTarget' 'CurrentUser' }
"Target user only" { AddParameter 'AppRemovalTarget' ($otherUsernameTextBox.Text.Trim()) }
"All users" { Add-Parameter 'AppRemovalTarget' 'AllUsers' }
"Current user only" { Add-Parameter 'AppRemovalTarget' 'CurrentUser' }
"Target user only" { Add-Parameter 'AppRemovalTarget' ($otherUsernameTextBox.Text.Trim()) }
}
}
}
@@ -681,7 +685,7 @@
# Apply dynamic tweaks
foreach ($tweakAction in @(Get-PendingTweakActions -Window $window -ShowAppliedTweaksMode:$showAppliedTweaksMode)) {
if ($tweakAction.Action -eq 'Apply') {
AddParameter $tweakAction.FeatureId
Add-Parameter $tweakAction.FeatureId
$null = $selectedForwardFeatureIds.Add([string]$tweakAction.FeatureId)
continue
}
@@ -695,27 +699,27 @@
$restorePointCheckBox = $window.FindName('RestorePointCheckBox')
if ($restorePointCheckBox -and $restorePointCheckBox.IsChecked) {
AddParameter 'CreateRestorePoint'
Add-Parameter 'CreateRestorePoint'
}
switch ($userSelectionCombo.SelectedIndex) {
0 { Write-Host "Selected user mode: current user ($(GetUserName))" }
0 { Write-Host "Selected user mode: current user ($(Get-UserName))" }
1 {
Write-Host "Selected user mode: $($otherUsernameTextBox.Text.Trim())"
AddParameter User ($otherUsernameTextBox.Text.Trim())
Add-Parameter User ($otherUsernameTextBox.Text.Trim())
}
2 {
Write-Host "Selected user mode: default user profile (Sysprep)"
AddParameter Sysprep
Add-Parameter Sysprep
}
}
SaveSettings
Save-Settings
$restartExplorerCheckBox = $window.FindName('RestartExplorerCheckBox')
$shouldRestartExplorer = $restartExplorerCheckBox -and $restartExplorerCheckBox.IsChecked
Show-ApplyModal -Owner $window -RestartExplorer $shouldRestartExplorer
Show-ApplyModal -Owner $window -InvokeRestartExplorer $shouldRestartExplorer
$window.Close()
})
@@ -737,12 +741,12 @@
$window.Add_Loaded({
try {
& $updateHomeContentPosition
Build-DynamicTweaks -Window $window -WinVersion $WinVersion
Load-CurrentTweakStateIntoUI -Window $window
New-DynamicTweakControls -Window $window -WinVersion $WinVersion
Set-CurrentTweakStateInUi -Window $window
Update-TweaksResponsiveColumns -Window $window
$lastUsedSettingsJson = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
$lastUsedSettingsJson = Import-JsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
$defaultsJson = Import-JsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
$script:SavedAppIds = Get-SavedAppIdsFromSettingsJson -SettingsJson $lastUsedSettingsJson
@@ -750,13 +754,13 @@
Register-TweakPresetControlStateHandlers -Window $window
Update-TweakPresetStates -Window $window
Load-AppsIntoMainUI -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn
Initialize-MainWindowApps -Window $window -AppsPanel $appsPanel -OnlyInstalledAppsBox $onlyInstalledAppsBox -LoadingAppsIndicator $loadingAppsIndicator -ImportConfigBtn $importConfigBtn
# Update Current User label
if ($userSelectionCombo -and $userSelectionCombo.Items.Count -gt 0) {
$currentUserItem = $userSelectionCombo.Items[0]
if ($currentUserItem -is [System.Windows.Controls.ComboBoxItem]) {
$currentUserItem.Content = "Current User ($(GetUserName))"
$currentUserItem.Content = "Current User ($(Get-UserName))"
}
}
@@ -809,8 +813,8 @@
})
# ---- Tweak presets wiring ----
$lastUsedSettingsJson = LoadJsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
$defaultsJson = LoadJsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
$lastUsedSettingsJson = Import-JsonFile -filePath $script:SavedSettingsFilePath -expectedVersion "1.0" -optionalFile
$defaultsJson = Import-JsonFile -filePath $script:DefaultSettingsFilePath -expectedVersion "1.0"
$script:DefaultTweakPresetMap = @{}
$script:LastUsedTweakPresetMap = @{}
$script:PrivacyTweakPresetMap = @{}
@@ -869,7 +873,7 @@
# ---- Preload app data ----
try {
$script:PreloadedAppData = LoadAppsDetailsFromJson -OnlyInstalled:$false -InstalledList $null -InitialCheckedFromJson:$false
$script:PreloadedAppData = Import-AppDetailsFromJson -OnlyInstalled:$false -InstalledList $null -InitialCheckedFromJson:$false
}
catch {
Write-Warning "Failed to preload apps list: $_"
+6 -3
View File
@@ -1,4 +1,7 @@
# Shows a Windows 11 styled custom message box
<#
.SYNOPSIS
Shows a themed Windows 11-style message box.
#>
function Show-MessageBox {
param (
[Parameter(Mandatory=$true)]
@@ -24,7 +27,7 @@ function Show-MessageBox {
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
$usesDarkMode = Get-SystemUsesDarkMode
# Determine owner window - use provided Owner, or fall back to main GUI window
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
@@ -69,7 +72,7 @@ function Show-MessageBox {
}
# Apply theme resources
SetWindowThemeResources -window $msgWindow -usesDarkMode $usesDarkMode
Set-WindowThemeResources -window $msgWindow -usesDarkMode $usesDarkMode
# Get UI elements
$titleText = $msgWindow.FindName('TitleText')
+7 -7
View File
@@ -15,7 +15,7 @@
Hashtable
Returns a Hashtable describing the user's choice. Possible shapes:
RestoreRegistry - @{ Result='RestoreRegistry'; Backup=<normalizedBackup> }
RestoreStartMenu - @{ Result='RestoreStartMenu'; StartMenuScope=<scope>;
Restore-StartMenu - @{ Result='Restore-StartMenu'; StartMenuScope=<scope>;
UseManualBackupFile=<bool>; BackupFilePath=<path|string> }
Cancelled - @{ Result='Cancelled' } (from New-RestoreDialogState)
#>
@@ -26,7 +26,7 @@ function Show-RestoreBackupDialog {
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase | Out-Null
$usesDarkMode = GetSystemUsesDarkMode
$usesDarkMode = Get-SystemUsesDarkMode
$ownerWindow = if ($Owner) { $Owner } else { $script:GuiWindow }
$overlay = $null
@@ -67,7 +67,7 @@ function Show-RestoreBackupDialog {
}
try {
SetWindowThemeResources -window $window -usesDarkMode $usesDarkMode
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
}
catch { }
@@ -129,7 +129,7 @@ function Show-RestoreBackupDialog {
param([string]$BackupFilePath)
$scopeInfo = & $getStartMenuScopeInfo
$backupTargetText.Text = GetFriendlyRegistryBackupTarget -Target $scopeInfo.Target
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target $scopeInfo.Target
$overviewSummaryText.Text = "This will replace the current Start Menu pinned apps layout for $($scopeInfo.SummaryText) with the selected backup."
$backupFileText.Text = Split-Path -Path $BackupFilePath -Leaf
@@ -276,7 +276,7 @@ function Show-RestoreBackupDialog {
$backupFileText.Text = Split-Path $SelectedBackupFilePath -Leaf
$backupCreatedText.Text = $createdText
$backupTargetText.Text = GetFriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
$overviewFeaturesSection.Visibility = if ($revertibleFeaturesList.Count -gt 0) { 'Visible' } else { 'Collapsed' }
$reappliedFeaturesItemsControl.ItemsSource = $reappliedFeaturesList
@@ -318,7 +318,7 @@ function Show-RestoreBackupDialog {
Write-Host "Backup file selected: $($openDialog.FileName)"
try {
$selectedBackup = Load-RegistryBackupFromFile -FilePath $openDialog.FileName
$selectedBackup = Import-RegistryBackup -FilePath $openDialog.FileName
if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) {
return
@@ -366,7 +366,7 @@ function Show-RestoreBackupDialog {
}
$window.Tag = @{
Result = 'RestoreStartMenu'
Result = 'Restore-StartMenu'
StartMenuScope = $scope
UseManualBackupFile = $useManualBackupFile
BackupFilePath = $state.SelectedStartMenuBackupFilePath
+7 -3
View File
@@ -1,3 +1,7 @@
<#
.SYNOPSIS
Shows the backup-restore dialog and performs the selected restore.
#>
function Show-RestoreBackupWindow {
param(
[System.Windows.Window]$Owner = $null
@@ -38,7 +42,7 @@ function Show-RestoreBackupWindow {
}
}
}
elseif ($dialogResult.Result -eq 'RestoreStartMenu') {
elseif ($dialogResult.Result -eq 'Restore-StartMenu') {
$scope = $dialogResult.StartMenuScope
$useManualBackupFile = ($dialogResult.UseManualBackupFile -eq $true)
$backupFilePath = $null
@@ -54,10 +58,10 @@ function Show-RestoreBackupWindow {
}
$result = if ($scope -eq 'AllUsers') {
RestoreStartMenuForAllUsers -BackupFilePath $backupFilePath
Restore-StartMenuForAllUsers -BackupFilePath $backupFilePath
}
else {
RestoreStartMenu -BackupFilePath $backupFilePath
Restore-StartMenu -BackupFilePath $backupFilePath
}
$resultEntries = @($result)