Compare commits

..
4 Commits
Author SHA1 Message Date
Chris Titus 5c104d02e2 Update automated update PR workflows and filter release notes 2026-07-16 13:39:33 -05:00
Chris TitusandGitHub 06f33e88ba Fix Show Installed Apps reliability and selection (#4850)
* Fix Windows PowerShell runspace cleanup callback

* Fix installed app package ID matching

* Synchronize installed app selection UI

* Streamline installed app detection

* Avoid installed app UI runspace deadlock
2026-07-16 13:30:12 -05:00
b9dee86694 refactor: style and align Install tab filter chips (#4848)
* refactor: implement FilterChipStyle for Install tab category filters

- Created a dedicated FilterChipStyle style block with custom padding, cursor, and height definitions

- Applied FilterChipStyle to all category filter buttons

- Added a new 'Filters' TextBlock section title aligned with the sidebar actions

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Chris Titus <contact@christitus.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-16 10:37:25 -05:00
OmarandGitHub 3d1904ebbe fix: wrap Tweaks header buttons to prevent layout clipping (#4847) 2026-07-16 10:29:16 -05:00
12 changed files with 352 additions and 63 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
name: Auto-merge Docs PRs
name: Auto-merge Automated Update PRs
on:
pull_request:
@@ -8,7 +8,7 @@ on:
jobs:
auto-merge:
if: github.event.pull_request.head.ref == 'docs-update' && (github.event.pull_request.user.login == 'ChrisTitusTech' || github.event.pull_request.user.login == 'github-actions[bot]')
if: (github.event.pull_request.head.ref == 'docs-update' || github.event.pull_request.head.ref == 'sponsors-update') && (github.event.pull_request.user.login == 'ChrisTitusTech' || github.event.pull_request.user.login == 'github-actions[bot]')
runs-on: ubuntu-latest
permissions:
pull-requests: write
+16 -1
View File
@@ -109,6 +109,21 @@ jobs:
config-name: release-drafter.yml
version: ${{ env.VERSION }}
- name: Remove Automated Updates from Release Notes
id: filter_notes
shell: pwsh
env:
RELEASE_NOTES: ${{ steps.generate_notes.outputs.body }}
run: |
$filteredNotes = ($env:RELEASE_NOTES -split '\r?\n' | Where-Object {
$_ -notmatch '(?i)Update (Generated Dev Docs|Sponsors)'
}) -join "`n"
$delimiter = [guid]::NewGuid().ToString()
"body<<$delimiter" >> $env:GITHUB_OUTPUT
$filteredNotes >> $env:GITHUB_OUTPUT
$delimiter >> $env:GITHUB_OUTPUT
- name: Create and Upload Release
id: create_release
uses: softprops/action-gh-release@v3
@@ -116,7 +131,7 @@ jobs:
tag_name: ${{ env.VERSION }}
name: Pre-Release ${{ env.VERSION }}
body: |
${{ steps.generate_notes.outputs.body }}
${{ steps.filter_notes.outputs.body }}
![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/winutil/${{ env.VERSION }}/winutil.ps1)
append_body: false
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
id: cpr
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.AUTO_MERGE }}
commit-message: 'Update sponsors in README'
title: 'chore: Update Sponsors README'
body: 'Automated update of sponsors section'
@@ -15,32 +15,36 @@ Function Invoke-WinUtilCurrentSystem {
)
if ($CheckBox -eq "choco") {
$apps = (choco list | Select-String -Pattern "^\S+").Matches.Value
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
$dependencies = @($sync.configs.applications.$($psitem.Key).choco -split ";")
if ($dependencies -in $apps) {
Write-Output $psitem.name
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
$packageId = ($_.Value.choco -split ";")[-1].Trim()
if ($packageId -ne "na" -and $packageId -in $apps) {
Write-Output $_.Key
}
}
}
if ($checkbox -eq "winget") {
$originalEncoding = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
$Sync.InstalledPrograms = @("winget", "msstore") | ForEach-Object {
winget list -s $psitem | Select-Object -skip 3 | ConvertFrom-String -PropertyNames "Name", "Id", "Version", "Available" -Delimiter '\s{2,}'
try {
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
$installedProgramOutput = @(winget list --accept-source-agreements --disable-interactivity 2>&1)
if ($LASTEXITCODE -ne 0) {
throw "winget list failed with exit code $LASTEXITCODE."
}
} finally {
[Console]::OutputEncoding = $originalEncoding
}
[Console]::OutputEncoding = $originalEncoding
$installedProgramText = $installedProgramOutput -join "`n"
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
$dependencies = @($sync.configs.applications.$($psitem.Key).winget -split ";") | ForEach-Object {
$psitem -replace "^msstore:", ""
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
$packageId = (($_.Value.winget -split ";")[-1] -replace "^msstore:", "").Trim()
if ([string]::IsNullOrWhiteSpace($packageId) -or $packageId -eq "na") {
return
}
if ($dependencies[-1] -in $sync.InstalledPrograms.Id) {
Write-Output $psitem.name
$packagePattern = "(?im)[^\S\r\n]{2,}$([regex]::Escape($packageId))(?=[^\S\r\n]{2,}|$)"
if ($installedProgramText -match $packagePattern) {
Write-Output $_.Key
}
}
}
+63 -27
View File
@@ -1,6 +1,5 @@
function Invoke-WPFGetInstalled {
<#
TODO: Add the Option to use Chocolatey as Engine
.SYNOPSIS
Invokes the function that gets the checkboxes to check in a new runspace
@@ -19,35 +18,72 @@ function Invoke-WPFGetInstalled {
return
}
$managerPreference = $sync.preferences.packagemanager
Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -ScriptBlock {
param (
[string]$checkbox,
[string]$managerPreference
$operation = [Hashtable]::Synchronized(@{
Checkboxes = @()
Error = $null
})
$completeAction = [Action[hashtable, string]]{
param(
[hashtable]$completedOperation,
[string]$completedCheckbox
)
$sync.ProcessRunning = $true
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" }
try {
if ($completedOperation.Error) {
Write-WinUtilLog -Level "ERROR" -Component "Install" -Message "Get installed state failed: $($completedOperation.Error)"
Write-Warning "Unable to get installed state: $($completedOperation.Error)"
return
}
if ($checkbox -eq "winget") {
Write-Host "Getting Installed Programs..."
switch ($managerPreference) {
"Choco"{$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox "choco"; break}
"Winget"{$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox $checkbox; break}
if ($completedCheckbox -eq "winget") {
foreach ($checkboxName in $completedOperation.Checkboxes) {
if (-not $sync.selectedApps.Contains($checkboxName)) {
$sync.selectedApps.Add($checkboxName)
}
}
Reset-WPFCheckBoxes -checkboxfilterpattern "WPFInstall*"
} else {
foreach ($checkboxName in $completedOperation.Checkboxes) {
$sync.$checkboxName.ischecked = $True
}
}
} finally {
$sync.ProcessRunning = $false
Set-WinUtilTaskbaritem -state "None"
}
}
$sync.ProcessRunning = $true
Set-WinUtilTaskbaritem -state "Indeterminate"
try {
Invoke-WPFRunspace -ParameterList @(
("managerPreference", $managerPreference),
("checkbox", $checkbox),
("operation", $operation),
("completeAction", $completeAction)
) -ScriptBlock {
param (
[string]$checkbox,
[string]$managerPreference,
[hashtable]$operation,
[Action[hashtable, string]]$completeAction
)
try {
if ($checkbox -eq "winget") {
switch ($managerPreference) {
"Choco" { $operation.Checkboxes = @(Invoke-WinUtilCurrentSystem -CheckBox "choco"); break }
"Winget" { $operation.Checkboxes = @(Invoke-WinUtilCurrentSystem -CheckBox $checkbox); break }
}
} elseif ($checkbox -eq "tweaks") {
$operation.Checkboxes = @(Invoke-WinUtilCurrentSystem -CheckBox $checkbox)
}
} catch {
$operation.Error = $_.Exception.Message
} finally {
$sync.Form.Dispatcher.BeginInvoke($completeAction, [object[]]@($operation, $checkbox)) | Out-Null
}
}
elseif ($checkbox -eq "tweaks") {
Write-Host "Getting Installed Tweaks..."
$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox $checkbox
}
$sync.form.Dispatcher.invoke({
foreach ($checkbox in $Checkboxes) {
$sync.$checkbox.ischecked = $True
}
})
Write-Host "Done..."
$sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" }
} catch {
$operation.Error = $_.Exception.Message
$completeAction.Invoke($operation, $checkbox)
}
}
+3 -2
View File
@@ -44,6 +44,8 @@ public sealed class WinUtilRunspaceCleanupState
public static class WinUtilRunspaceCleanup
{
public static readonly System.Threading.WaitOrTimerCallback Callback = Cleanup;
public static void Cleanup(object state, bool timedOut)
{
var cleanupState = state as WinUtilRunspaceCleanupState;
@@ -89,8 +91,7 @@ public static class WinUtilRunspaceCleanup
$cleanupState = [WinUtilRunspaceCleanupState]::new()
$cleanupState.PowerShell = $powershell
$cleanupState.Handle = $handle
$cleanupCallback = [System.Threading.WaitOrTimerCallback][WinUtilRunspaceCleanup]::Cleanup
[System.Threading.ThreadPool]::RegisterWaitForSingleObject($handle.AsyncWaitHandle, $cleanupCallback, $cleanupState, -1, $true) | Out-Null
[System.Threading.ThreadPool]::RegisterWaitForSingleObject($handle.AsyncWaitHandle, [WinUtilRunspaceCleanup]::Callback, $cleanupState, -1, $true) | Out-Null
# Return the handle
return $handle
@@ -7,11 +7,21 @@ function Invoke-WPFSelectedCheckboxesUpdate ($type, $checkboxName) {
'^WPFAppx' { 'selectedAppx' }
}
$selectionChanged = $false
if ($type -eq "Add") {
if (-not $sync.$listName.Contains($checkboxName)) {
$sync.$listName.Add($checkboxName)
$selectionChanged = $true
}
} else {
$sync.$listName.Remove($checkboxName)
$selectionChanged = $sync.$listName.Remove($checkboxName)
}
if ($listName -eq "selectedApps" -and $selectionChanged) {
$sync.WPFselectedAppsButton.Content = "Selected Apps: $($sync.selectedApps.Count)"
$sync.selectedAppsstackPanel.Children.Clear()
$sync.selectedApps | Sort-Object | ForEach-Object {
Add-SelectedAppsMenuItem -name $sync.configs.applicationsHashtable.$_.Content -key $_
}
}
}
+4
View File
@@ -140,6 +140,10 @@ Describe "Invoke-WPFRunspace behavior" {
$runspaceScript | Should -Not -Match '\$script:powershell'
$runspaceScript | Should -Not -Match '\$script:handle'
}
It "exposes a strongly typed cleanup callback" {
([WinUtilRunspaceCleanup]::Callback -is [System.Threading.WaitOrTimerCallback]) | Should -BeTrue
}
}
Describe "Public runspace callers" {
+70
View File
@@ -6,12 +6,82 @@ $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
BeforeAll {
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
. (Join-Path $script:repoRoot "functions\private\Invoke-WinUtilCurrentSystem.ps1")
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilRegistry.ps1")
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilService.ps1")
function winget {
param([Parameter(ValueFromRemainingArguments = $true)]$Arguments)
}
function choco {
param([Parameter(ValueFromRemainingArguments = $true)]$Arguments)
}
function Write-WinUtilLog { }
}
Describe "Invoke-WinUtilCurrentSystem installed apps" {
BeforeEach {
$script:sync = [Hashtable]::Synchronized(@{
configs = [pscustomobject]@{
applicationsHashtable = @{
WPFInstallGit = [pscustomobject]@{ winget = "Git.Git"; choco = "git" }
WPFInstallChatGPT = [pscustomobject]@{ winget = "msstore:9NT1R1C2HH7J"; choco = "na" }
WPFInstallMissing = [pscustomobject]@{ winget = "Git"; choco = "missing" }
}
}
})
Mock winget {
$global:LASTEXITCODE = 0
$script:wingetArguments = @($Arguments)
@(
"Name Id Version Source",
"--------------------------------",
"Git Git.Git 2.0 winget",
"ChatGPT 9NT1R1C2HH7J 1.0 msstore"
)
}
Mock choco {
$script:chocoArguments = @($Arguments)
@("Chocolatey v2", "git 2.0", "2 packages installed.")
}
}
AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name wingetArguments -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name chocoArguments -Scope Script -ErrorAction SilentlyContinue
}
It "matches single standard and Microsoft Store package IDs" {
$result = @(Invoke-WinUtilCurrentSystem -CheckBox "winget")
$result | Should -HaveCount 2
$result | Should -Contain "WPFInstallGit"
$result | Should -Contain "WPFInstallChatGPT"
$result | Should -Not -Contain "WPFInstallMissing"
Should -Invoke -CommandName winget -Times 1 -Exactly
$script:wingetArguments | Should -Be @("list", "--accept-source-agreements", "--disable-interactivity")
}
It "fails promptly when Winget cannot list applications" {
Mock winget {
$global:LASTEXITCODE = 1
"winget failed"
}
{ Invoke-WinUtilCurrentSystem -CheckBox "winget" } | Should -Throw "winget list failed with exit code 1."
}
It "matches the primary Chocolatey package ID in one list call" {
$result = @(Invoke-WinUtilCurrentSystem -CheckBox "choco")
$result | Should -Be @("WPFInstallGit")
Should -Invoke -CommandName choco -Times 1 -Exactly
$script:chocoArguments | Should -Be @("list")
}
}
Describe "Set-WinUtilRegistry" {
BeforeEach {
$script:testPathResults = @{}
+110
View File
@@ -61,6 +61,7 @@ namespace System.Windows.Controls
. (Join-Path $script:repoRoot "functions\private\Update-WinUtilSelections.ps1")
. (Join-Path $script:repoRoot "functions\private\Reset-WPFCheckBoxes.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFGetInstalled.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFSelectedCheckboxesUpdate.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFToggleAllCategories.ps1")
@@ -68,6 +69,24 @@ namespace System.Windows.Controls
function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent)
}
function Invoke-WPFRunspace {
param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock)
}
function Invoke-WPFUIThread {
param([scriptblock]$ScriptBlock)
}
function Invoke-WinUtilCurrentSystem {
param($CheckBox)
}
function Set-WinUtilTaskbaritem {
param($state)
}
function Test-WinUtilPackageManager {
param([switch]$winget)
}
function Write-WinUtilLog {
param($Message, $Level, $Component)
}
function script:New-WinUtilFakeCheckBox {
param([bool]$IsChecked = $false)
@@ -180,6 +199,9 @@ Describe "Invoke-WPFSelectedCheckboxesUpdate" {
@($script:sync.selectedToggles) | Should -Be @("WPFToggleDarkMode")
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
@($script:sync.selectedAppx) | Should -Be @("WPFAppxExample")
$script:sync.WPFselectedAppsButton.Content | Should -Be "Selected Apps: 1"
$script:sync.selectedAppsstackPanel.Children.Count | Should -Be 1
$script:sync.selectedAppsstackPanel.Children[0].Key | Should -Be "WPFInstallGit"
}
It "removes checkbox keys from the matching selected lists" {
@@ -200,6 +222,94 @@ Describe "Invoke-WPFSelectedCheckboxesUpdate" {
$script:sync.selectedToggles.Count | Should -Be 0
$script:sync.selectedFeatures.Count | Should -Be 0
$script:sync.selectedAppx.Count | Should -Be 0
$script:sync.WPFselectedAppsButton.Content | Should -Be "Selected Apps: 0"
$script:sync.selectedAppsstackPanel.Children.Count | Should -Be 0
}
}
Describe "Invoke-WPFGetInstalled selection state" {
BeforeEach {
New-WinUtilUiStateTestContext
$script:sync.ProcessRunning = $false
$script:sync.ChocoRadioButton = [pscustomobject]@{ IsChecked = $false }
$script:sync.preferences = [pscustomobject]@{ packagemanager = "Winget" }
$script:sync.WPFInstallGit = New-WinUtilFakeCheckBox
$dispatcher = [pscustomobject]@{}
$dispatcher | Add-Member -MemberType ScriptMethod -Name BeginInvoke -Value {
param($Action, [object[]]$Arguments)
$Action.DynamicInvoke($Arguments)
}
$script:sync.Form = [pscustomobject]@{ Dispatcher = $dispatcher }
$script:capturedGetInstalledScriptBlock = $null
$script:capturedGetInstalledParameters = @{}
Mock Test-WinUtilPackageManager { "installed" }
Mock Invoke-WinUtilCurrentSystem { @("WPFInstallGit") }
Mock Set-WinUtilTaskbaritem { }
Mock Write-WinUtilLog { }
Mock Write-Warning { }
Mock Invoke-WPFRunspace {
$script:capturedGetInstalledScriptBlock = $ScriptBlock
foreach ($parameter in $ParameterList) {
$script:capturedGetInstalledParameters[$parameter[0]] = $parameter[1]
}
}
}
AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
Remove-Variable -Name capturedGetInstalledScriptBlock -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name capturedGetInstalledParameters -Scope Script -ErrorAction SilentlyContinue
}
It "updates the selected app model, checkbox, count, and popup" {
Invoke-WPFGetInstalled -CheckBox "winget"
& $script:capturedGetInstalledScriptBlock `
-checkbox "winget" `
-managerPreference "Winget" `
-operation $script:capturedGetInstalledParameters.operation `
-completeAction $script:capturedGetInstalledParameters.completeAction
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
$script:sync.WPFInstallGit.IsChecked | Should -BeTrue
$script:sync.WPFselectedAppsButton.Content | Should -Be "Selected Apps: 1"
$script:sync.selectedAppsstackPanel.Children.Count | Should -Be 1
$script:sync.selectedAppsstackPanel.Children[0].Key | Should -Be "WPFInstallGit"
}
It "clears the running state when detection fails" {
Mock Invoke-WinUtilCurrentSystem { throw "detection failed" }
Invoke-WPFGetInstalled -CheckBox "winget"
& $script:capturedGetInstalledScriptBlock `
-checkbox "winget" `
-managerPreference "Winget" `
-operation $script:capturedGetInstalledParameters.operation `
-completeAction $script:capturedGetInstalledParameters.completeAction
$script:sync.ProcessRunning | Should -BeFalse
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "Install" -and
$Message -eq "Get installed state failed: detection failed"
}
Should -Invoke -CommandName Set-WinUtilTaskbaritem -Times 1 -Exactly -ParameterFilter { $state -eq "None" }
}
It "clears the running state when the worker cannot be queued" {
Mock Invoke-WPFRunspace { throw "queue failed" }
Invoke-WPFGetInstalled -CheckBox "winget"
$script:sync.ProcessRunning | Should -BeFalse
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "Install" -and
$Message -eq "Get installed state failed: queue failed"
}
Should -Invoke -CommandName Set-WinUtilTaskbaritem -Times 1 -Exactly -ParameterFilter { $state -eq "None" }
}
}
-1
View File
@@ -435,7 +435,6 @@ Describe "XAML and sync wiring" {
"appPopup",
"appPopupSelectedApp",
"ItemsControl",
"InstalledPrograms",
"ImportInProgress",
"ScriptsInstallPrograms",
"keys",
+52 -12
View File
@@ -964,6 +964,39 @@
</Setter.Value>
</Setter>
</Style>
<!-- Filter Chip Style — used by the Install tab category filter buttons -->
<Style x:Key="FilterChipStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="12,0,12,0"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="ChipBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{DynamicResource ButtonBorderThickness}"
CornerRadius="{DynamicResource ButtonCornerRadius}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundPressedColor}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundMouseoverColor}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundSelectedColor}"/>
<Setter Property="Foreground" Value="DimGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Background="{DynamicResource MainBackgroundColor}" ShowGridLines="False" Name="WPFMainGrid" Width="Auto" Height="Auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
@@ -1282,16 +1315,23 @@
<!-- Quick Category Search Chips -->
<WrapPanel Grid.Row="0" Orientation="Horizontal" Margin="5,5,5,5" Name="WPFSearchChips">
<Button Name="WPFSearchChipAll" Content="All" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipBrowsers" Content="Browsers" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipCommunications" Content="Communications" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipDevelopment" Content="Development" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipGames" Content="Games" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipMicrosoftTools" Content="Microsoft Tools" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipMultimediaTools" Content="Multimedia Tools" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipProTools" Content="Pro Tools" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipSelfhostedTools" Content="Selfhosted Tools" Width="Auto" Height="Auto" Margin="2"/>
<Button Name="WPFSearchChipUtilities" Content="Utilities" Width="Auto" Height="Auto" Margin="2"/>
<TextBlock Text="Filters"
FontSize="{DynamicResource HeaderFontSize}"
FontFamily="{DynamicResource HeaderFontFamily}"
Foreground="{DynamicResource LabelboxForegroundColor}"
Background="Transparent"
VerticalAlignment="Center"
Margin="15,0,8,0"/>
<Button Name="WPFSearchChipAll" Content="All" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipBrowsers" Content="Browsers" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipCommunications" Content="Communications" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipDevelopment" Content="Development" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipGames" Content="Games" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipMicrosoftTools" Content="Microsoft Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipMultimediaTools" Content="Multimedia Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipProTools" Content="Pro Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipSelfhostedTools" Content="Selfhosted Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipUtilities" Content="Utilities" Style="{StaticResource FilterChipStyle}"/>
</WrapPanel>
<Grid Grid.Row="1" Margin="{DynamicResource TabContentMargin}">
@@ -1326,14 +1366,14 @@
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Vertical" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5">
<Label Content="Recommended Selections:" FontSize="{DynamicResource FontSize}" VerticalAlignment="Center" Margin="2"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
<Button Name="WPFstandard" Content=" Standard " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFminimal" Content=" Minimal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFAdvanced" Content=" Advanced " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFClearTweaksSelection" Content=" Clear " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFGetInstalledTweaks" Content=" Get Installed Tweaks " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFAppxRemoval" Content=" AppX Removal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
</StackPanel>
</WrapPanel>
</StackPanel>
<Grid Name="tweakspanel" Grid.Row="1">