feat: add config-driven AppX package removal tab

- Add config/appx.json with curated list of removable AppX packages

- Add AppX Removal tab (WPFTab6) with Select All, Clear, and Remove buttons

- Reuse existing Invoke-WPFUIElements engine for minimal code footprint

- Add Invoke-WPFAppxRemoval.ps1 for background runspace package removal

- Add theme colors for the new tab button in light and dark modes
This commit is contained in:
GabiNun2
2026-06-21 17:57:41 +03:00
parent 58a81b106c
commit e8c1401cce
9 changed files with 300 additions and 0 deletions
+11
View File
@@ -23,6 +23,17 @@ Get-ChildItem config | ForEach-Object {
$obj = [pscustomobject]$fixed $obj = [pscustomobject]$fixed
} }
if ($_.Name -eq "appx.json") {
$fixed = [ordered]@{}
foreach ($p in $obj.PSObject.Properties) {
$keyName = "WPFAppx$($p.Name -replace '\.', '_')"
$val = $p.Value
$val | Add-Member -MemberType NoteProperty -Name "PackageId" -Value $p.Name -Force
$fixed[$keyName] = $val
}
$obj = [pscustomobject]$fixed
}
$json = $obj | ConvertTo-Json -Depth 10 $json = $obj | ConvertTo-Json -Depth 10
$sync.configs[$_.BaseName] = $obj $sync.configs[$_.BaseName] = $obj
+146
View File
@@ -0,0 +1,146 @@
{
"Microsoft.WindowsFeedbackHub": {
"Category": "Telemetry & Diagnostics",
"Content": "Feedback Hub",
"Description": "Feedback Hub is a Microsoft application for gathering user feedback on Windows.",
"Panel": "0"
},
"Microsoft.BingNews": {
"Category": "Bing & Web Services",
"Content": "Bing News",
"Description": "Pre-installed Bing News application.",
"Panel": "0"
},
"Microsoft.BingSearch": {
"Category": "Bing & Web Services",
"Content": "Bing Search",
"Description": "Pre-installed Bing Search application.",
"Panel": "0"
},
"Microsoft.BingWeather": {
"Category": "Bing & Web Services",
"Content": "Bing Weather",
"Description": "Pre-installed Bing Weather application.",
"Panel": "0"
},
"Clipchamp.Clipchamp": {
"Category": "Microsoft Apps",
"Content": "Clipchamp",
"Description": "Clipchamp is Microsoft's pre-installed video editor.",
"Panel": "1"
},
"Microsoft.Todos": {
"Category": "Utilities & Productivity",
"Content": "To Do",
"Description": "Microsoft To Do task manager application.",
"Panel": "1"
},
"Microsoft.PowerAutomateDesktop": {
"Category": "Utilities & Productivity",
"Content": "Power Automate",
"Description": "Power Automate Desktop flow designer.",
"Panel": "1"
},
"Microsoft.MicrosoftSolitaireCollection": {
"Category": "Xbox & Gaming",
"Content": "Microsoft Solitaire Collection",
"Description": "Pre-installed Solitaire collection game.",
"Panel": "0"
},
"Microsoft.WindowsSoundRecorder": {
"Category": "Utilities & Productivity",
"Content": "Sound Recorder",
"Description": "Windows Voice Recorder / Sound Recorder app.",
"Panel": "1"
},
"Microsoft.MicrosoftStickyNotes": {
"Category": "Utilities & Productivity",
"Content": "Sticky Notes",
"Description": "Sticky Notes application for quick note taking.",
"Panel": "1"
},
"Microsoft.Windows.DevHome": {
"Category": "Microsoft Apps",
"Content": "Dev Home",
"Description": "Dev Home control center and developer tools app.",
"Panel": "1"
},
"Microsoft.Paint": {
"Category": "Utilities & Productivity",
"Content": "Paint",
"Description": "Classic image editing application.",
"Panel": "1"
},
"Microsoft.OutlookForWindows": {
"Category": "Microsoft Apps",
"Content": "Outlook for Windows",
"Description": "Pre-installed new Outlook application.",
"Panel": "1"
},
"Microsoft.WindowsAlarms": {
"Category": "Utilities & Productivity",
"Content": "Windows Clock",
"Description": "Windows Alarm & Clock application.",
"Panel": "1"
},
"Microsoft.GetHelp": {
"Category": "Microsoft Apps",
"Content": "Get Help",
"Description": "Microsoft Get Help assistant app.",
"Panel": "1"
},
"Microsoft.ZuneMusic": {
"Category": "Microsoft Apps",
"Content": "Windows Media Player",
"Description": "Pre-installed Zune Music / Media Player application.",
"Panel": "1"
},
"MicrosoftCorporationII.QuickAssist": {
"Category": "Utilities & Productivity",
"Content": "Quick Assist",
"Description": "Microsoft Quick Assist remote support app.",
"Panel": "1"
},
"MSTeams": {
"Category": "Microsoft Apps",
"Content": "Microsoft Teams",
"Description": "Microsoft Teams chat and collaboration tool.",
"Panel": "1"
},
"Microsoft.XboxIdentityProvider": {
"Category": "Xbox & Gaming",
"Content": "Xbox Identity Provider",
"Description": "Provides authentication services for Xbox Live connectivity.",
"Panel": "0"
},
"Microsoft.XboxSpeechToTextOverlay": {
"Category": "Xbox & Gaming",
"Content": "Xbox Speech to Text Overlay",
"Description": "Accessibility overlay for converting Xbox party chat to text.",
"Panel": "0"
},
"Microsoft.GamingApp": {
"Category": "Xbox & Gaming",
"Content": "Xbox App",
"Description": "The main Xbox App for gaming on Windows.",
"Panel": "0"
},
"Microsoft.Xbox.TCUI": {
"Category": "Xbox & Gaming",
"Content": "Xbox TCUI",
"Description": "Xbox Live UI component.",
"Panel": "0"
},
"Microsoft.XboxGamingOverlay": {
"Category": "Xbox & Gaming",
"Content": "Xbox Game Bar",
"Description": "Xbox Game Bar overlay for gaming captures and social tools.",
"Panel": "0"
},
"Microsoft.MicrosoftOfficeHub": {
"Category": "Microsoft Apps",
"Content": "Office (365) Hub",
"Description": "Microsoft 365 dashboard application.",
"Panel": "1"
}
}
+4
View File
@@ -65,11 +65,13 @@
"ButtonConfigBackgroundColor": "#F7F7F7", "ButtonConfigBackgroundColor": "#F7F7F7",
"ButtonUpdatesBackgroundColor": "#F7F7F7", "ButtonUpdatesBackgroundColor": "#F7F7F7",
"ButtonWin11ISOBackgroundColor": "#F7F7F7", "ButtonWin11ISOBackgroundColor": "#F7F7F7",
"ButtonAppxBackgroundColor": "#F7F7F7",
"ButtonInstallForegroundColor": "#232629", "ButtonInstallForegroundColor": "#232629",
"ButtonTweaksForegroundColor": "#232629", "ButtonTweaksForegroundColor": "#232629",
"ButtonConfigForegroundColor": "#232629", "ButtonConfigForegroundColor": "#232629",
"ButtonUpdatesForegroundColor": "#232629", "ButtonUpdatesForegroundColor": "#232629",
"ButtonWin11ISOForegroundColor": "#232629", "ButtonWin11ISOForegroundColor": "#232629",
"ButtonAppxForegroundColor": "#232629",
"ButtonBackgroundColor": "#F5F5F5", "ButtonBackgroundColor": "#F5F5F5",
"ButtonBackgroundPressedColor": "#1A1A1A", "ButtonBackgroundPressedColor": "#1A1A1A",
"ButtonBackgroundMouseoverColor": "#C2C2C2", "ButtonBackgroundMouseoverColor": "#C2C2C2",
@@ -105,11 +107,13 @@
"ButtonConfigBackgroundColor": "#444444", "ButtonConfigBackgroundColor": "#444444",
"ButtonUpdatesBackgroundColor": "#555555", "ButtonUpdatesBackgroundColor": "#555555",
"ButtonWin11ISOBackgroundColor": "#666666", "ButtonWin11ISOBackgroundColor": "#666666",
"ButtonAppxBackgroundColor": "#777777",
"ButtonInstallForegroundColor": "#F7F7F7", "ButtonInstallForegroundColor": "#F7F7F7",
"ButtonTweaksForegroundColor": "#F7F7F7", "ButtonTweaksForegroundColor": "#F7F7F7",
"ButtonConfigForegroundColor": "#F7F7F7", "ButtonConfigForegroundColor": "#F7F7F7",
"ButtonUpdatesForegroundColor": "#F7F7F7", "ButtonUpdatesForegroundColor": "#F7F7F7",
"ButtonWin11ISOForegroundColor": "#F7F7F7", "ButtonWin11ISOForegroundColor": "#F7F7F7",
"ButtonAppxForegroundColor": "#F7F7F7",
"ButtonBackgroundColor": "#1E3747", "ButtonBackgroundColor": "#1E3747",
"ButtonBackgroundPressedColor": "#F7F7F7", "ButtonBackgroundPressedColor": "#F7F7F7",
"ButtonBackgroundMouseoverColor": "#3B4252", "ButtonBackgroundMouseoverColor": "#3B4252",
@@ -0,0 +1,50 @@
function Invoke-WPFAppxRemoval {
<#
.SYNOPSIS
Removes the selected AppX packages in a background runspace.
#>
if ($sync.ProcessRunning) {
$msg = "A process is currently running. Please wait for it to finish."
[System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
return
}
$selected = @($sync.selectedAppx)
if ($selected.Count -eq 0) {
$msg = "Please select the AppX packages you wish to remove."
[System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
return
}
$totalSteps = $selected.Count
$completedSteps = 0
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
$handle = Invoke-WPFRunspace -ParameterList @(("selected", $selected), ("completedSteps", $completedSteps), ("totalSteps", $totalSteps)) -ScriptBlock {
param($selected, $completedSteps, $totalSteps)
$sync.ProcessRunning = $true
for ($i = 0; $i -lt $selected.Count; $i++) {
$key = $selected[$i]
$appInfo = $sync.configs.appxHashtable.$key
$packageId = $appInfo.PackageId
$friendlyName = $appInfo.content
Set-WinUtilProgressBar -Label "Removing $friendlyName" -Percent ($completedSteps / $totalSteps * 100)
Remove-WinUtilAPPX -Name $packageId
$completedSteps++
$progress = $completedSteps / $totalSteps
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value $progress }
}
Set-WinUtilProgressBar -Label "AppX Removal finished" -Percent 100
$sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "================================="
Write-Host "-- AppX Removal Finished ---"
Write-Host "================================="
}
}
+17
View File
@@ -65,6 +65,23 @@ function Invoke-WPFButton {
"WPFUpdatessecurity" {Invoke-WPFUpdatessecurity} "WPFUpdatessecurity" {Invoke-WPFUpdatessecurity}
"WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"} "WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"}
"WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"} "WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"}
"WPFSelectAllAppx" {
$sync.configs.appxHashtable.Keys | ForEach-Object {
if ($sync.$_) {
$sync.$_.IsChecked = $true
}
}
}
"WPFClearAppxSelection" {
$sync.configs.appxHashtable.Keys | ForEach-Object {
if ($sync.$_) {
$sync.$_.IsChecked = $false
}
}
}
"WPFRemoveSelectedAppx" {
Invoke-WPFAppxRemoval
}
"WPFCloseButton" {$sync.Form.Close(); Write-Host "Bye bye!"} "WPFCloseButton" {$sync.Form.Close(); Write-Host "Bye bye!"}
"WPFMinimizeButton" {$sync.Form.WindowState = [Windows.WindowState]::Minimized} "WPFMinimizeButton" {$sync.Form.WindowState = [Windows.WindowState]::Minimized}
"WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen} "WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen}
@@ -30,6 +30,7 @@ function Invoke-WPFSelectedCheckboxesUpdate{
elseif ($appKey.StartsWith("WPFTweaks")) { "Tweaks" } elseif ($appKey.StartsWith("WPFTweaks")) { "Tweaks" }
elseif ($appKey.StartsWith("WPFToggle")) { "Toggle" } elseif ($appKey.StartsWith("WPFToggle")) { "Toggle" }
elseif ($appKey.StartsWith("WPFFeature")) { "Feature" } elseif ($appKey.StartsWith("WPFFeature")) { "Feature" }
elseif ($appKey.StartsWith("WPFAppx")) { "Appx" }
else { "na" } else { "na" }
switch ($group) { switch ($group) {
@@ -81,6 +82,16 @@ function Invoke-WPFSelectedCheckboxesUpdate{
$sync.selectedFeatures.Remove($appKey) $sync.selectedFeatures.Remove($appKey)
} }
} }
"Appx" {
if ($type -eq "Add") {
if (!$sync.selectedAppx.Contains($appKey)) {
$sync.selectedAppx.Add($appKey)
}
}
else{
$sync.selectedAppx.Remove($appKey)
}
}
default { default {
Write-Host "Unknown group for checkbox: $($appKey)" Write-Host "Unknown group for checkbox: $($appKey)"
} }
+7
View File
@@ -86,6 +86,11 @@ $sync.configs.applications.PSObject.Properties | ForEach-Object {
$sync.configs.applicationsHashtable[$_.Name] = $_.Value $sync.configs.applicationsHashtable[$_.Name] = $_.Value
} }
$sync.configs.appxHashtable = @{}
$sync.configs.appx.PSObject.Properties | ForEach-Object {
$sync.configs.appxHashtable[$_.Name] = $_.Value
}
Set-Preferences Set-Preferences
if ($Preset) { if ($Preset) {
@@ -188,6 +193,8 @@ Invoke-WPFUIElements -configVariable $sync.configs.tweaks -targetGridName "tweak
Invoke-WPFUIElements -configVariable $sync.configs.feature -targetGridName "featurespanel" -columncount 2 Invoke-WPFUIElements -configVariable $sync.configs.feature -targetGridName "featurespanel" -columncount 2
Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2
# Future implementation: Add Windows Version to updates panel # Future implementation: Add Windows Version to updates panel
#Invoke-WPFUIElements -configVariable $sync.configs.updates -targetGridName "updatespanel" -columncount 1 #Invoke-WPFUIElements -configVariable $sync.configs.updates -targetGridName "updatespanel" -columncount 1
+1
View File
@@ -71,6 +71,7 @@ $sync.selectedApps = [System.Collections.Generic.List[string]]::new()
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new() $sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
$sync.selectedToggles = [System.Collections.Generic.List[string]]::new() $sync.selectedToggles = [System.Collections.Generic.List[string]]::new()
$sync.selectedFeatures = [System.Collections.Generic.List[string]]::new() $sync.selectedFeatures = [System.Collections.Generic.List[string]]::new()
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
$sync.currentTab = "Install" $sync.currentTab = "Install"
$sync.selectedAppsStackPanel $sync.selectedAppsStackPanel
$sync.selectedAppsPopup $sync.selectedAppsPopup
+53
View File
@@ -1005,6 +1005,14 @@
</TextBlock> </TextBlock>
</ToggleButton.Content> </ToggleButton.Content>
</ToggleButton> </ToggleButton>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="Auto" MinWidth="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonAppxBackgroundColor}" Foreground="{DynamicResource ButtonAppxForegroundColor}" FontWeight="Bold" Name="WPFTab6BT">
<ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonAppxForegroundColor}">
<Underline>A</Underline>ppX Removal
</TextBlock>
</ToggleButton.Content>
</ToggleButton>
</StackPanel> </StackPanel>
<!-- Search Bar and Action Buttons --> <!-- Search Bar and Action Buttons -->
@@ -1690,6 +1698,51 @@
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="AppX" Visibility="Collapsed" Name="WPFTab6">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Grid.Row="0" Margin="{DynamicResource TabContentMargin}">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Vertical" Grid.Row="0" Grid.Column="0" Margin="5">
<Label Content="Selections:" FontSize="{DynamicResource FontSize}" VerticalAlignment="Center" Margin="2"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
<Button Name="WPFSelectAllAppx" Content=" Select All " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFClearAppxSelection" Content=" Clear Selection " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
</StackPanel>
</StackPanel>
<Grid Name="appxpanel" Grid.Row="1">
<!-- Dynamic AppX package checkboxes -->
</Grid>
<Border Grid.Row="2" Style="{StaticResource BorderStyle}" Margin="5,15,5,5">
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Padding="10" TextWrapping="Wrap" Foreground="{DynamicResource MainForegroundColor}">
Note: Select the pre-installed Windows AppX packages you wish to remove and click 'Remove Selected'.
<LineBreak/>These packages are removed for the current user and all new user profiles.
</TextBlock>
</StackPanel>
</Border>
</Grid>
</ScrollViewer>
<Border Grid.Row="1" Background="{DynamicResource MainBackgroundColor}" BorderBrush="{DynamicResource BorderColor}" BorderThickness="1" CornerRadius="5" HorizontalAlignment="Stretch" Padding="10">
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<Button Name="WPFRemoveSelectedAppx" Content="Remove Selected" Margin="5" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
</WrapPanel>
</Border>
</Grid>
</TabItem>
</TabControl> </TabControl>
</Grid> </Grid>
</Window> </Window>