diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8fc9d3d0..58c9a91f 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -19,4 +19,4 @@
## Checklist
- [ ] My code adheres to the coding and style guidelines of the project.
- [ ] I have commented my code, particularly in hard-to-understand areas.
-- [ ] I have made corresponding changes to the documentation.
\ No newline at end of file
+- [ ] I have made corresponding changes to the documentation.
diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml
index 7bb81319..0c96acd4 100644
--- a/.github/workflows/label-pr.yaml
+++ b/.github/workflows/label-pr.yaml
@@ -18,7 +18,7 @@ jobs:
script: |
const prBody = context.payload.pull_request.body || '';
const labelsToAdd = [];
-
+
if (/\[x\]\s*New feature/i.test(prBody)) {
labelsToAdd.push('new feature');
}
@@ -34,7 +34,7 @@ jobs:
if (/\[x\]\s*UI\/UX improvement/i.test(prBody)) {
labelsToAdd.push('ui update');
}
-
+
if (labelsToAdd.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
@@ -42,4 +42,4 @@ jobs:
issue_number: context.payload.pull_request.number,
labels: labelsToAdd
});
- }
\ No newline at end of file
+ }
diff --git a/config/appnavigation.json b/config/appnavigation.json
index d7782162..de3b976e 100644
--- a/config/appnavigation.json
+++ b/config/appnavigation.json
@@ -38,25 +38,39 @@
"Order": "2",
"Description": "Use Chocolatey for package management"
},
+ "WPFCollapseAllCategories": {
+ "Content": "Collapse All Categories",
+ "Category": "__Selection",
+ "Type": "Button",
+ "Order": "1",
+ "Description": "Collapse all application categories"
+ },
+ "WPFExpandAllCategories": {
+ "Content": "Expand All Categories",
+ "Category": "__Selection",
+ "Type": "Button",
+ "Order": "2",
+ "Description": "Expand all application categories"
+ },
"WPFClearInstallSelection": {
"Content": "Clear Selection",
"Category": "__Selection",
"Type": "Button",
- "Order": "1",
+ "Order": "3",
"Description": "Clear the selection of applications"
},
"WPFGetInstalled": {
"Content": "Get Installed",
"Category": "__Selection",
"Type": "Button",
- "Order": "2",
+ "Order": "4",
"Description": "Show installed applications"
},
"WPFselectedAppsButton": {
"Content": "Selected Apps: 0",
"Category": "__Selection",
"Type": "Button",
- "Order": "3",
+ "Order": "5",
"Description": "Show the selected applications"
}
}
diff --git a/config/themes.json b/config/themes.json
index b44ae419..3797e5db 100644
--- a/config/themes.json
+++ b/config/themes.json
@@ -1,8 +1,8 @@
{
"shared":{
- "AppEntryWidth": "130",
+ "AppEntryWidth": "200",
"AppEntryFontSize": "11",
- "AppEntryMargin": "1,1,1,1",
+ "AppEntryMargin": "1,0,1,0",
"AppEntryBorderThickness": "0",
"CustomDialogFontSize": "12",
"CustomDialogFontSizeHeader": "14",
@@ -91,7 +91,7 @@
"AppInstallOverlayBackgroundColor":"#2E3135",
"ComboBoxForegroundColor": "#F7F7F7",
"ComboBoxBackgroundColor": "#1E3747",
- "LabelboxForegroundColor": "#0567ff",
+ "LabelboxForegroundColor": "#5bdcff",
"MainForegroundColor": "#F7F7F7",
"MainBackgroundColor": "#232629",
"LabelBackgroundColor": "#232629",
diff --git a/docs/content/dev/architecture.md b/docs/content/dev/architecture.md
index 5c38facb..c5e84781 100644
--- a/docs/content/dev/architecture.md
+++ b/docs/content/dev/architecture.md
@@ -546,5 +546,5 @@ Outputs `winutil.ps1` in the root directory.
---
-**Last Updated**: January 2026
-**Maintainers**: Chris Titus Tech and contributors
\ No newline at end of file
+**Last Updated**: January 2026
+**Maintainers**: Chris Titus Tech and contributors
diff --git a/docs/content/dev/tweaks/Essential-Tweaks/Hibernation.md b/docs/content/dev/tweaks/Essential-Tweaks/Hibernation.md
index d81776ba..399304c2 100644
--- a/docs/content/dev/tweaks/Essential-Tweaks/Hibernation.md
+++ b/docs/content/dev/tweaks/Essential-Tweaks/Hibernation.md
@@ -30,7 +30,7 @@
"powercfg.exe /hibernate on"
],
```
-
+
## Registry Changes
Applications and System Components store and retrieve configuration data to modify windows settings, so we can use the registry to change many settings in one place.
diff --git a/docs/content/dev/tweaks/z--Advanced-Tweaks---CAUTION/RemoveOneDrive.md b/docs/content/dev/tweaks/z--Advanced-Tweaks---CAUTION/RemoveOneDrive.md
index 89b160f7..457ad452 100644
--- a/docs/content/dev/tweaks/z--Advanced-Tweaks---CAUTION/RemoveOneDrive.md
+++ b/docs/content/dev/tweaks/z--Advanced-Tweaks---CAUTION/RemoveOneDrive.md
@@ -13,7 +13,7 @@
Write-Host \"Uninstalling OneDrive...\"
Start-Process 'C:\\Windows\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
-
+
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
Write-Host \"Removing leftover OneDrive Files...\"
Stop-Process -Name FileCoAuth,Explorer
diff --git a/docs/content/faq.md b/docs/content/faq.md
index c5f5fe13..26431ecd 100644
--- a/docs/content/faq.md
+++ b/docs/content/faq.md
@@ -297,5 +297,5 @@ Can't find your answer? Try these resources:
---
-**Last Updated**: January 2026
+**Last Updated**: January 2026
**Found this helpful?** Consider starring the project on [GitHub](https://github.com/ChrisTitusTech/winutil)!
diff --git a/docs/content/userguide/_index.md b/docs/content/userguide/_index.md
index f50ccad3..74afb89e 100644
--- a/docs/content/userguide/_index.md
+++ b/docs/content/userguide/_index.md
@@ -54,7 +54,7 @@ Apply optimizations for performance, privacy, and usability. Choose from preset
Quick fixes for common Windows problems:
- Reset network settings
-- Fix Windows Update issues
+- Fix Windows Update issues
- Repair system files
- Access legacy Windows panels
@@ -152,7 +152,7 @@ This User Guide covers everything you need to know:
1. **[Getting Started](getting-started/)** - Installation, first run, basic usage
2. **[Application Store](store/)** - Installing software, using presets
-3. **[Tweaks](tweaks/)** - System optimizations and customizations
+3. **[Tweaks](tweaks/)** - System optimizations and customizations
4. **[Features & Fixes](features/)** - Troubleshooting tools and utilities
5. **[MicroWin](microwin/)** - Creating custom Windows ISOs
6. **[Updates](updates/)** - Managing Windows Update behavior
diff --git a/functions/private/Find-AppsByNameOrDescription.ps1 b/functions/private/Find-AppsByNameOrDescription.ps1
index 3300d88b..4bf55cb7 100644
--- a/functions/private/Find-AppsByNameOrDescription.ps1
+++ b/functions/private/Find-AppsByNameOrDescription.ps1
@@ -13,37 +13,68 @@ function Find-AppsByNameOrDescription {
# Reset the visibility if the search string is empty or the search is cleared
if ([string]::IsNullOrWhiteSpace($SearchString)) {
$sync.ItemsControl.Items | ForEach-Object {
+ # Each item is a StackPanel container
$_.Visibility = [Windows.Visibility]::Visible
- $_.Children | ForEach-Object {
- if ($null -ne $_) {
- $_.Visibility = [Windows.Visibility]::Visible
+
+ if ($_.Children.Count -ge 2) {
+ $categoryLabel = $_.Children[0]
+ $wrapPanel = $_.Children[1]
+
+ # Keep category label visible
+ $categoryLabel.Visibility = [Windows.Visibility]::Visible
+
+ # Respect the collapsed state of categories (indicated by + prefix)
+ if ($categoryLabel.Content -like "+*") {
+ $wrapPanel.Visibility = [Windows.Visibility]::Collapsed
+ } else {
+ $wrapPanel.Visibility = [Windows.Visibility]::Visible
}
+ # Show all apps within the category
+ $wrapPanel.Children | ForEach-Object {
+ $_.Visibility = [Windows.Visibility]::Visible
+ }
}
}
return
}
+
+ # Perform search
$sync.ItemsControl.Items | ForEach-Object {
- # Ensure ToggleButtons remain visible
- if ($_.Tag -like "CategoryToggleButton") {
- $_.Visibility = [Windows.Visibility]::Visible
- return
- }
- # Hide all CategoryWrapPanel and ToggleButton
- $_.Visibility = [Windows.Visibility]::Collapsed
- if ($_.Tag -like "CategoryWrapPanel_*") {
- # Search for Apps that match the search string
- $_.Children | Foreach-Object {
+ # Each item is a StackPanel container with Children[0] = label, Children[1] = WrapPanel
+ if ($_.Children.Count -ge 2) {
+ $categoryLabel = $_.Children[0]
+ $wrapPanel = $_.Children[1]
+ $categoryHasMatch = $false
+
+ # Keep category label visible
+ $categoryLabel.Visibility = [Windows.Visibility]::Visible
+
+ # Search through apps in this category
+ $wrapPanel.Children | ForEach-Object {
$appEntry = $sync.configs.applicationsHashtable.$($_.Tag)
if ($appEntry.Content -like "*$SearchString*" -or $appEntry.Description -like "*$SearchString*") {
- # Show the App and the parent CategoryWrapPanel if the string is found
+ # Show the App and mark that this category has a match
$_.Visibility = [Windows.Visibility]::Visible
- $_.parent.Visibility = [Windows.Visibility]::Visible
+ $categoryHasMatch = $true
}
else {
$_.Visibility = [Windows.Visibility]::Collapsed
}
}
+
+ # If category has matches, show the WrapPanel and update the category label to expanded state
+ if ($categoryHasMatch) {
+ $wrapPanel.Visibility = [Windows.Visibility]::Visible
+ $_.Visibility = [Windows.Visibility]::Visible
+ # Update category label to show expanded state (-)
+ if ($categoryLabel.Content -like "+*") {
+ $categoryLabel.Content = $categoryLabel.Content -replace "^\+ ", "- "
+ }
+ } else {
+ # Hide the entire category container if no matches
+ $_.Visibility = [Windows.Visibility]::Collapsed
+ }
}
}
}
diff --git a/functions/private/Initialize-InstallAppArea.ps1 b/functions/private/Initialize-InstallAppArea.ps1
index bb605ab8..85bd50a7 100644
--- a/functions/private/Initialize-InstallAppArea.ps1
+++ b/functions/private/Initialize-InstallAppArea.ps1
@@ -41,13 +41,13 @@
$itemsControl.VerticalAlignment = 'Stretch'
$scrollViewer.Content = $itemsControl
- # Enable virtualization for the ItemsControl to improve performance (It's hard to test if this is actually working, so if you know what you're doing, please check this)
+ # Use WrapPanel to create dynamic columns based on AppEntryWidth and window width
$itemsPanelTemplate = New-Object Windows.Controls.ItemsPanelTemplate
- $factory = New-Object Windows.FrameworkElementFactory ([Windows.Controls.VirtualizingStackPanel])
+ $factory = New-Object Windows.FrameworkElementFactory ([Windows.Controls.WrapPanel])
+ $factory.SetValue([Windows.Controls.WrapPanel]::OrientationProperty, [Windows.Controls.Orientation]::Horizontal)
+ $factory.SetValue([Windows.Controls.WrapPanel]::HorizontalAlignmentProperty, [Windows.HorizontalAlignment]::Left)
$itemsPanelTemplate.VisualTree = $factory
$itemsControl.ItemsPanel = $itemsPanelTemplate
- $itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::IsVirtualizingProperty, $true)
- $itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::VirtualizationModeProperty, [Windows.Controls.VirtualizationMode]::Recycling)
# Add the Border containing the App Area to the target Grid
$targetGrid.Children.Add($Border) | Out-Null
diff --git a/functions/private/Initialize-InstallCategoryAppList.ps1 b/functions/private/Initialize-InstallCategoryAppList.ps1
index 6daded27..ce78e193 100644
--- a/functions/private/Initialize-InstallCategoryAppList.ps1
+++ b/functions/private/Initialize-InstallCategoryAppList.ps1
@@ -15,22 +15,6 @@ function Initialize-InstallCategoryAppList {
$TargetElement,
$Apps
)
- function Add-Category {
- param(
- [string]$Category,
- [Windows.Controls.ItemsControl]$TargetElement
- )
-
- $toggleButton = New-Object Windows.Controls.Label
- $toggleButton.Content = "$Category"
- $toggleButton.Tag = "CategoryToggleButton"
- $toggleButton.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "HeaderFontSize")
- $toggleButton.SetResourceReference([Windows.Controls.Control]::FontFamilyProperty, "HeaderFontFamily")
- $sync.$Category = $toggleButton
-
- $null = $TargetElement.Items.Add($toggleButton)
- }
-
# Pre-group apps by category
$appsByCategory = @{}
@@ -42,17 +26,71 @@ function Initialize-InstallCategoryAppList {
$appsByCategory[$category] += $appKey
}
foreach ($category in $($appsByCategory.Keys | Sort-Object)) {
- Add-Category -Category $category -TargetElement $TargetElement
+ # Create a container for category label + apps
+ $categoryContainer = New-Object Windows.Controls.StackPanel
+ $categoryContainer.Orientation = "Vertical"
+ $categoryContainer.Margin = New-Object Windows.Thickness(0, 0, 0, 0)
+ $categoryContainer.HorizontalAlignment = [Windows.HorizontalAlignment]::Stretch
+
+ # Bind Width to the ItemsControl's ActualWidth to force full-row layout in WrapPanel
+ $binding = New-Object Windows.Data.Binding
+ $binding.Path = New-Object Windows.PropertyPath("ActualWidth")
+ $binding.RelativeSource = New-Object Windows.Data.RelativeSource([Windows.Data.RelativeSourceMode]::FindAncestor, [Windows.Controls.ItemsControl], 1)
+ [void][Windows.Data.BindingOperations]::SetBinding($categoryContainer, [Windows.FrameworkElement]::WidthProperty, $binding)
+
+ # Add category label to container
+ $toggleButton = New-Object Windows.Controls.Label
+ $toggleButton.Content = "- $Category"
+ $toggleButton.Tag = "CategoryToggleButton"
+ $toggleButton.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "HeaderFontSize")
+ $toggleButton.SetResourceReference([Windows.Controls.Control]::FontFamilyProperty, "HeaderFontFamily")
+ $toggleButton.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "LabelboxForegroundColor")
+ $toggleButton.Cursor = [System.Windows.Input.Cursors]::Hand
+ $toggleButton.HorizontalAlignment = [Windows.HorizontalAlignment]::Stretch
+ $sync.$Category = $toggleButton
+
+ # Add click handler to toggle category visibility
+ $toggleButton.Add_MouseLeftButtonUp({
+ param($sender, $e)
+
+ # Find the parent StackPanel (categoryContainer)
+ $categoryContainer = $sender.Parent
+ if ($categoryContainer -and $categoryContainer.Children.Count -ge 2) {
+ # The WrapPanel is the second child
+ $wrapPanel = $categoryContainer.Children[1]
+
+ # Toggle visibility
+ if ($wrapPanel.Visibility -eq [Windows.Visibility]::Visible) {
+ $wrapPanel.Visibility = [Windows.Visibility]::Collapsed
+ # Change - to +
+ $sender.Content = $sender.Content -replace "^- ", "+ "
+ } else {
+ $wrapPanel.Visibility = [Windows.Visibility]::Visible
+ # Change + to -
+ $sender.Content = $sender.Content -replace "^\+ ", "- "
+ }
+ }
+ })
+
+ $null = $categoryContainer.Children.Add($toggleButton)
+
+ # Add wrap panel for apps to container
$wrapPanel = New-Object Windows.Controls.WrapPanel
$wrapPanel.Orientation = "Horizontal"
- $wrapPanel.HorizontalAlignment = "Stretch"
- $wrapPanel.VerticalAlignment = "Center"
- $wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 20)
+ $wrapPanel.HorizontalAlignment = "Left"
+ $wrapPanel.VerticalAlignment = "Top"
+ $wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 0)
$wrapPanel.Visibility = [Windows.Visibility]::Visible
$wrapPanel.Tag = "CategoryWrapPanel_$category"
- $null = $TargetElement.Items.Add($wrapPanel)
- $appsByCategory[$category] |Sort-Object | ForEach-Object {
- $sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_)
+
+ $null = $categoryContainer.Children.Add($wrapPanel)
+
+ # Add the entire category container to the target element
+ $null = $TargetElement.Items.Add($categoryContainer)
+
+ # Add apps to the wrap panel
+ $appsByCategory[$category] | Sort-Object | ForEach-Object {
+ $sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_)
}
}
}
diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1
index 68f91e2e..1743a671 100644
--- a/functions/public/Invoke-WPFButton.ps1
+++ b/functions/public/Invoke-WPFButton.ps1
@@ -23,6 +23,8 @@ function Invoke-WPFButton {
"WPFInstall" {Invoke-WPFInstall}
"WPFUninstall" {Invoke-WPFUnInstall}
"WPFInstallUpgrade" {Invoke-WPFInstallUpgrade}
+ "WPFCollapseAllCategories" {Invoke-WPFToggleAllCategories -Action "Collapse"}
+ "WPFExpandAllCategories" {Invoke-WPFToggleAllCategories -Action "Expand"}
"WPFStandard" {Invoke-WPFPresets "Standard" -checkboxfilterpattern "WPFTweak*"}
"WPFMinimal" {Invoke-WPFPresets "Minimal" -checkboxfilterpattern "WPFTweak*"}
"WPFClearTweaksSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFTweak*"}
diff --git a/functions/public/Invoke-WPFToggleAllCategories.ps1 b/functions/public/Invoke-WPFToggleAllCategories.ps1
new file mode 100644
index 00000000..960a265d
--- /dev/null
+++ b/functions/public/Invoke-WPFToggleAllCategories.ps1
@@ -0,0 +1,51 @@
+function Invoke-WPFToggleAllCategories {
+ <#
+ .SYNOPSIS
+ Expands or collapses all categories in the Install tab
+
+ .PARAMETER Action
+ The action to perform: "Expand" or "Collapse"
+
+ .DESCRIPTION
+ This function iterates through all category containers in the Install tab
+ and expands or collapses their WrapPanels while updating the toggle button labels
+ #>
+
+ param(
+ [Parameter(Mandatory=$true)]
+ [ValidateSet("Expand", "Collapse")]
+ [string]$Action
+ )
+
+ try {
+ if ($null -eq $sync.ItemsControl) {
+ Write-Warning "ItemsControl not initialized"
+ return
+ }
+
+ $targetVisibility = if ($Action -eq "Expand") { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed }
+ $targetPrefix = if ($Action -eq "Expand") { "-" } else { "+" }
+ $sourcePrefix = if ($Action -eq "Expand") { "+" } else { "-" }
+
+ # Iterate through all items in the ItemsControl
+ $sync.ItemsControl.Items | ForEach-Object {
+ $categoryContainer = $_
+
+ # Check if this is a category container (StackPanel with children)
+ if ($categoryContainer -is [System.Windows.Controls.StackPanel] -and $categoryContainer.Children.Count -ge 2) {
+ # Get the WrapPanel (second child)
+ $wrapPanel = $categoryContainer.Children[1]
+ $wrapPanel.Visibility = $targetVisibility
+
+ # Update the label to show the correct state
+ $categoryLabel = $categoryContainer.Children[0]
+ if ($categoryLabel.Content -like "$sourcePrefix*") {
+ $categoryLabel.Content = $categoryLabel.Content -replace "^$sourcePrefix ", "$targetPrefix "
+ }
+ }
+ }
+ }
+ catch {
+ Write-Error "Error toggling categories: $_"
+ }
+}
diff --git a/xaml/inputXML.xaml b/xaml/inputXML.xaml
index c241d399..b9db5655 100644
--- a/xaml/inputXML.xaml
+++ b/xaml/inputXML.xaml
@@ -23,7 +23,7 @@
-
+
@@ -85,7 +85,7 @@