Compare commits

...
24 Commits
Author SHA1 Message Date
51cb3811dd Add category search chips to Install tab for improved filtering (#4826)
* Add Quick Category Search Chips to Install tab

Add category shortcut chips on the Install tab that insert the category
name into the existing search box. Append each app's category as a
description tag so the existing description search filters by category.
Reuses the standard button style and existing search; no search, install,
or preset logic is changed.

* Fix install category chip filtering

* Fix category filter refresh behavior

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-15 20:54:22 -05:00
975616b58f Fix search bar overflowing behind icon buttons at narrow window sizes (#4831)
* fix(search): make search bar stretch instead of overflowing at narrow window sizes

* fix(ui): sync runtime MinWidth override with XAML value

Add_Loaded was hardcoding $sync.Form.MinWidth back to 1000 after
load, overriding the 1150 set in inputXML.xaml and letting the
window shrink below the space the search bar/toolbar layout needs.

* fix(ui): address responsive search review feedback

* fix(ui): enforce usable toolbar width

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-15 20:41:41 -05:00
11783c75a8 fix(dns): scope leaked ToggleButton style and theme the ComboBox dropdown popup (#4835)
Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-15 17:09:10 -05:00
13a18eeeb2 Add maximaze button (#4841)
* Add maximize button logic and update assets

* Add maximize button with theming

* delelte logo

* Center and restyle the window control buttons

* Fix window state test setup

---------

Co-authored-by: aran628 <aran2014+@gmail.com>
Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-15 16:59:51 -05:00
Chris TitusandGitHub 822003d87f Fix AppX detection and add package restore support (#4842)
* Fix installed AppX package detection

* Add AppX package restore workflow

* Add AppX operation progress feedback

* Fix AppX operation launch race

* Handle AppX workflow failures
2026-07-15 15:57:56 -05:00
c8c3c7869f Add window-level progress indicator for Run Tweaks and Undo (#4830)
* feat(tweaks): add window-level progress indicator for Run Tweaks and Undo

* Hide completed tweaks progress on next action

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-15 14:17:42 -05:00
Paweł GwozdowskiandGitHub 157a66b14d Update Invoke-WPFUIElements.ps1 (#4824) 2026-07-13 13:20:53 -05:00
9791386f53 fix: resolve silent failures in AppX removal and PS7 DISM bugs (#4822)
* fix: resolve silent failures in AppX removal and PS7 DISM bugs

- Fixed a bug where Remove-AppxPackage failed silently in PowerShell 7 due to pipeline binding issues. We now explicitly loop through Get-AppxPackage results and pass PackageFullName directly.
- Added wildcard matching to Get-AppxPackage and Get-AppxProvisionedPackage to reliably locate packages regardless of short name vs family name variations.
- Offloaded Get-AppxProvisionedPackage and Remove-AppxProvisionedPackage execution to Windows PowerShell 5.1 (powershell.exe) to bypass PowerShell 7 DISM COM exceptions ("Class not registered") and performance hangs.
- Deduplicated AppX removal logic by calling Remove-WinUtilAPPX directly from Invoke-WPFAppxRemoval instead of copying the implementation.

* refactor(Remove-WinUtilAPPX): use script block to improve readability

* feat: enhance AppX removal process and add provisioned package removal function

* fix(tests): update AppX removal tests

This also adds tests for the new Remove-WinUtilProvisionedAPPX functionality

* Move AppX removal under Tweaks

* Address AppX removal review feedback

* Complete AppX removal error handling

* Prevent AppX cleanup confirmation prompts

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-13 13:16:35 -05:00
Chris TitusandGitHub c8e756dc11 chore: Update generated dev docs (#4838) 2026-07-13 11:40:20 -05:00
Sean (ANGRYxScotsman)andGitHub 9e21c8f4a7 Updated ConsumerFeatures description and small dev doc edit (#4817)
* Update tweaks.json

* Update _index.md

* Update _index.md
2026-07-13 11:39:49 -05:00
Chris TitusandGitHub 8cd8679512 chore: Update generated dev docs (#4837) 2026-07-13 11:37:52 -05:00
ac11c73b64 feat(legacy-panels): add Firewall, Security, Programs, and Mouse to L… (#4809)
* feat(legacy-panels): add Firewall, Security, Programs, and Mouse to Legacy Windows Panels

- Added `firewall.cpl`, `wscui.cpl`, `appwiz.cpl`, and `main.cpl` to `config/feature.json`

* chore(legacy-panels): alphabetize panel entries

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-13 11:37:22 -05:00
KarimandGitHub 9d67514bf9 feat(apps): add gsudo (#4810)
- Added gsudo to the Pro Tools category to provide a native sudo equivalent for Windows terminals.
2026-07-13 10:26:44 -05:00
Chris TitusandGitHub fea9f4e869 chore: Update generated dev docs (#4836) 2026-07-13 10:26:33 -05:00
1aa0dd90ef prevent-automatic-download-of-applications-associated-with-device-metadata (#4827)
* add tweak

* add devdocs

* add devdocs

---------

Co-authored-by: matthewkasiski <matt@matthewkasiski.me>
2026-07-13 10:25:58 -05:00
Yayo RazoandGitHub 90a19685f5 fix(install): use correctly-scoped $app instead of undefined $Apps in app entry rendering (#4834) 2026-07-13 04:23:24 -05:00
007995c112 Improve install app card UI (#4771)
Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-11 11:21:59 -05:00
Chris Titus 0dcd361c51 Remove slash commands for contributors 2026-07-09 14:25:50 -05:00
eduardodepaivaandGitHub aeaef46e30 Fix names in applications.json. (#4819) 2026-07-08 10:25:15 -05:00
Ivan LepekhaandGitHub e0dfeb4f4d Remove Lightshot entry from applications.json (#4808)
Removed Lightshot application entry from applications.json
2026-07-04 09:52:55 -05:00
Chris Titus 1c7ee06bbe chore: skip automated updates in changelog 2026-07-04 00:58:24 -05:00
ed6de94c46 Update sponsors in README (#4544)
Co-authored-by: ChrisTitusTech <7896101+ChrisTitusTech@users.noreply.github.com>
2026-07-04 00:52:10 -05:00
Chris TitusandGitHub 640b02868a fix(iso): resolve windows update re-enabling failures on first boot (#4807)
* fix iso update service reenable

* test allow font scale sync state
2026-07-04 00:50:45 -05:00
29b269201d Add font scaling keyboard and mouse shortcuts (#4781)
* Add font scaling shortcuts

* Fix font scaling shortcut modifiers

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-07-04 00:32:39 -05:00
92 changed files with 1712 additions and 330 deletions
+1
View File
@@ -71,6 +71,7 @@ jobs:
labels: | labels: |
automated automated
documentation documentation
skip-changelog
- name: Check outputs - name: Check outputs
run: | run: |
@@ -1,77 +0,0 @@
name: Issue slash commands
on:
issue_comment:
types: [created, edited]
jobs:
issueCommands:
# Skip this job if the comment was created/edited on a PR
if: ${{ !github.event.issue.pull_request }}
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: none
contents: read
steps:
- name: Process slash command
uses: actions/github-script@v9
with:
script: |
const allowedUsers = ["ChrisTitusTech", "og-mrk", "Marterich", "MyDrift-user", "Real-MullaC", "CodingWonders", "GabiNun2", "FluffyPunk"];
const commenter = context.payload.comment.user.login;
// Authorization check first — before any parsing of comment content
if (!allowedUsers.includes(commenter)) {
console.log(`User ${commenter} is not in the allowlist. Skipping.`);
return;
}
// Read comment body as data, never interpolated into shell
const body = context.payload.comment.body;
const issueNumber = context.issue.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
// /label 'name' or /label name
const labelMatch = body.match(/\/label\s+'([^']+)'|\/label\s+(\S+?)(?:\s|$)/);
if (labelMatch) {
const labelName = (labelMatch[1] || labelMatch[2]).trim();
console.log(`Adding label: ${labelName}`);
await github.rest.issues.addLabels({
owner, repo, issue_number: issueNumber,
labels: [labelName],
});
}
// /unlabel 'name' or /unlabel name
const unlabelMatch = body.match(/\/unlabel\s+'([^']+)'|\/unlabel\s+(\S+?)(?:\s|$)/);
if (unlabelMatch) {
const labelName = (unlabelMatch[1] || unlabelMatch[2]).trim();
console.log(`Removing label: ${labelName}`);
await github.rest.issues.removeLabel({
owner, repo, issue_number: issueNumber,
name: labelName,
});
}
// /close (optionally with 'not planned')
if (body.includes('/close')) {
const stateReason = body.includes('not planned') ? 'not_planned' : 'completed';
console.log(`Closing issue (reason: ${stateReason})`);
await github.rest.issues.update({
owner, repo, issue_number: issueNumber,
state: 'closed',
state_reason: stateReason,
});
}
// /open or /reopen
if (body.includes('/open') || body.includes('/reopen')) {
console.log('Reopening issue');
await github.rest.issues.update({
owner, repo, issue_number: issueNumber,
state: 'open',
});
}
+3 -1
View File
@@ -32,7 +32,9 @@ jobs:
body: 'Automated update of sponsors section' body: 'Automated update of sponsors section'
branch: sponsors-update branch: sponsors-update
delete-branch: true delete-branch: true
labels: automated labels: |
automated
skip-changelog
- name: Check outputs - name: Check outputs
run: | run: |
+1 -1
View File
@@ -77,7 +77,7 @@ See https://github.com/ChrisTitusTech/winutil/blob/main/.github/CONTRIBUTING.md
These are the sponsors that help keep this project alive with monthly contributions. These are the sponsors that help keep this project alive with monthly contributions.
<!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https:&#x2F;&#x2F;github.com&#x2F;dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https:&#x2F;&#x2F;github.com&#x2F;mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https:&#x2F;&#x2F;github.com&#x2F;jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https:&#x2F;&#x2F;github.com&#x2F;robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https:&#x2F;&#x2F;github.com&#x2F;paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https:&#x2F;&#x2F;github.com&#x2F;djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https:&#x2F;&#x2F;github.com&#x2F;anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https:&#x2F;&#x2F;github.com&#x2F;FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https:&#x2F;&#x2F;github.com&#x2F;DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https:&#x2F;&#x2F;github.com&#x2F;DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https:&#x2F;&#x2F;github.com&#x2F;KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https:&#x2F;&#x2F;github.com&#x2F;andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><!-- sponsors --> <!-- sponsors --><a href="https://github.com/ysaito8015"><img src="https:&#x2F;&#x2F;github.com&#x2F;ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https:&#x2F;&#x2F;github.com&#x2F;dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https:&#x2F;&#x2F;github.com&#x2F;mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https:&#x2F;&#x2F;github.com&#x2F;jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https:&#x2F;&#x2F;github.com&#x2F;robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https:&#x2F;&#x2F;github.com&#x2F;paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https:&#x2F;&#x2F;github.com&#x2F;djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https:&#x2F;&#x2F;github.com&#x2F;anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https:&#x2F;&#x2F;github.com&#x2F;FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https:&#x2F;&#x2F;github.com&#x2F;DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https:&#x2F;&#x2F;github.com&#x2F;DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https:&#x2F;&#x2F;github.com&#x2F;KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https:&#x2F;&#x2F;github.com&#x2F;andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/johanwildeboer"><img src="https:&#x2F;&#x2F;github.com&#x2F;johanwildeboer.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/lukas346"><img src="https:&#x2F;&#x2F;github.com&#x2F;lukas346.png" width="60px" alt="User avatar: Wook" /></a><a href="https://github.com/tsv31"><img src="https:&#x2F;&#x2F;github.com&#x2F;tsv31.png" width="60px" alt="User avatar: Sorin" /></a><!-- sponsors -->
--- ---
+12 -12
View File
@@ -530,6 +530,15 @@
"winget": "TechPowerUp.GPU-Z", "winget": "TechPowerUp.GPU-Z",
"foss": false "foss": false
}, },
"gsudo": {
"category": "Pro Tools",
"choco": "gsudo",
"content": "gsudo",
"description": "gsudo is a sudo equivalent for Windows. It allows you to run commands with elevated administrative privileges directly within the current console window.",
"link": "https://github.com/gerardog/gsudo",
"winget": "gerardog.gsudo",
"foss": true
},
"helium": { "helium": {
"category": "Browsers", "category": "Browsers",
"choco": "helium", "choco": "helium",
@@ -719,15 +728,6 @@
"winget": "XBMCFoundation.Kodi", "winget": "XBMCFoundation.Kodi",
"foss": true "foss": true
}, },
"lightshot": {
"category": "Multimedia Tools",
"choco": "lightshot",
"content": "Lightshot",
"description": "The fastest way to take a customizable screenshot.",
"link": "https://app.prntscr.com/",
"winget": "Skillbrains.Lightshot",
"foss": false
},
"lazygit": { "lazygit": {
"category": "Development", "category": "Development",
"choco": "lazygit", "choco": "lazygit",
@@ -1207,7 +1207,7 @@
"processmonitor": { "processmonitor": {
"category": "Microsoft Tools", "category": "Microsoft Tools",
"choco": "procexp", "choco": "procexp",
"content": "SysInternals Process Monitor", "content": "Process Monitor",
"description": "SysInternals Process Monitor is an advanced monitoring tool that shows real-time file system, registry, and process/thread activity.", "description": "SysInternals Process Monitor is an advanced monitoring tool that shows real-time file system, registry, and process/thread activity.",
"link": "https://docs.microsoft.com/en-us/sysinternals/downloads/procmon", "link": "https://docs.microsoft.com/en-us/sysinternals/downloads/procmon",
"winget": "Microsoft.Sysinternals.ProcessMonitor", "winget": "Microsoft.Sysinternals.ProcessMonitor",
@@ -1396,7 +1396,7 @@
"tcpview": { "tcpview": {
"category": "Microsoft Tools", "category": "Microsoft Tools",
"choco": "tcpview", "choco": "tcpview",
"content": "SysInternals TCPView", "content": "TCPView",
"description": "SysInternals TCPView is a network monitoring tool that displays a detailed list of all TCP and UDP endpoints on your system.", "description": "SysInternals TCPView is a network monitoring tool that displays a detailed list of all TCP and UDP endpoints on your system.",
"link": "https://docs.microsoft.com/en-us/sysinternals/downloads/tcpview", "link": "https://docs.microsoft.com/en-us/sysinternals/downloads/tcpview",
"winget": "Microsoft.Sysinternals.TCPView", "winget": "Microsoft.Sysinternals.TCPView",
@@ -1513,7 +1513,7 @@
"ungoogled": { "ungoogled": {
"category": "Browsers", "category": "Browsers",
"choco": "ungoogled-chromium", "choco": "ungoogled-chromium",
"content": "Ungoogled", "content": "Ungoogled Chromium",
"description": "Ungoogled Chromium is a version of Chromium without Google's integration for enhanced privacy and control.", "description": "Ungoogled Chromium is a version of Chromium without Google's integration for enhanced privacy and control.",
"link": "https://github.com/Eloston/ungoogled-chromium", "link": "https://github.com/Eloston/ungoogled-chromium",
"winget": "eloston.ungoogled-chromium", "winget": "eloston.ungoogled-chromium",
+60 -30
View File
@@ -4,203 +4,232 @@
"Content": "Feedback Hub", "Content": "Feedback Hub",
"Description": "Allows users to submit bug reports, feature suggestions, and diagnostic data directly to Microsoft.", "Description": "Allows users to submit bug reports, feature suggestions, and diagnostic data directly to Microsoft.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.WindowsFeedbackHub" "PackageId": "Microsoft.WindowsFeedbackHub",
"StoreId": "9NBLGGH4R32N"
}, },
"WPFAppxMicrosoft_GetHelp": { "WPFAppxMicrosoft_GetHelp": {
"Category": "Microsoft Apps", "Category": "Microsoft Apps",
"Content": "Get Help", "Content": "Get Help",
"Description": "Provides access to automated troubleshooting guides, support documentation, and direct Microsoft customer assistance.", "Description": "Provides access to automated troubleshooting guides, support documentation, and direct Microsoft customer assistance.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.GetHelp" "PackageId": "Microsoft.GetHelp",
"StoreId": "9PKDZBMV1H3T"
}, },
"WPFAppxMicrosoft_OutlookForWindows": { "WPFAppxMicrosoft_OutlookForWindows": {
"Category": "Microsoft Apps", "Category": "Microsoft Apps",
"Content": "Outlook for Windows", "Content": "Outlook for Windows",
"Description": "Provides modern email management, calendar scheduling, and contact organization features.", "Description": "Provides modern email management, calendar scheduling, and contact organization features.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.OutlookForWindows" "PackageId": "Microsoft.OutlookForWindows",
"StoreId": "9NRX63209R7B"
}, },
"WPFAppxMSTeams": { "WPFAppxMSTeams": {
"Category": "Microsoft Apps", "Category": "Microsoft Apps",
"Content": "Microsoft Teams", "Content": "Microsoft Teams",
"Description": "Facilitates instant messaging, video conferencing, file sharing, and workspace collaboration.", "Description": "Facilitates instant messaging, video conferencing, file sharing, and workspace collaboration.",
"Panel": "0", "Panel": "0",
"PackageId": "MSTeams" "PackageId": "MSTeams",
"StoreId": "XP8BT8DW290MPQ"
}, },
"WPFAppxClipchamp_Clipchamp": { "WPFAppxClipchamp_Clipchamp": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Clipchamp", "Content": "Clipchamp",
"Description": "Provides a user-friendly video editor with built-in templates, effects, and timeline editing tools.", "Description": "Provides a user-friendly video editor with built-in templates, effects, and timeline editing tools.",
"Panel": "0", "Panel": "0",
"PackageId": "Clipchamp.Clipchamp" "PackageId": "Clipchamp.Clipchamp",
"StoreId": "9P1J8S7CCWWT"
}, },
"WPFAppxMicrosoft_MicrosoftOfficeHub": { "WPFAppxMicrosoft_MicrosoftOfficeHub": {
"Category": "Microsoft Apps", "Category": "Microsoft Apps",
"Content": "Microsoft 365", "Content": "Microsoft 365",
"Description": "Serves as a centralized launcher and dashboard for accessing cloud-based Microsoft 365 apps and recent documents.", "Description": "Serves as a centralized launcher and dashboard for accessing cloud-based Microsoft 365 apps and recent documents.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.MicrosoftOfficeHub" "PackageId": "Microsoft.MicrosoftOfficeHub",
"StoreId": "9WZDNCRD29V9"
}, },
"WPFAppxMicrosoft_ZuneMusic": { "WPFAppxMicrosoft_ZuneMusic": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Media Player", "Content": "Media Player",
"Description": "Plays local audio and video files with modern playlist management and casting capabilities.", "Description": "Plays local audio and video files with modern playlist management and casting capabilities.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.ZuneMusic" "PackageId": "Microsoft.ZuneMusic",
"StoreId": "9WZDNCRFJ3PT"
}, },
"WPFAppxMicrosoft_BingSearch": { "WPFAppxMicrosoft_BingSearch": {
"Category": "Bing & Web Services", "Category": "Bing & Web Services",
"Content": "Bing Search", "Content": "Bing Search",
"Description": "Integrates Microsoft Bing search capabilities and web services directly into the operating system.", "Description": "Integrates Microsoft Bing search capabilities and web services directly into the operating system.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.BingSearch" "PackageId": "Microsoft.BingSearch",
"StoreId": "9NZBF4GT040C"
}, },
"WPFAppxMicrosoftCorporationII_QuickAssist": { "WPFAppxMicrosoftCorporationII_QuickAssist": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Quick Assist", "Content": "Quick Assist",
"Description": "Enables secure remote technical support and screen sharing over an internet connection.", "Description": "Enables secure remote technical support and screen sharing over an internet connection.",
"Panel": "0", "Panel": "0",
"PackageId": "MicrosoftCorporationII.QuickAssist" "PackageId": "MicrosoftCorporationII.QuickAssist",
"StoreId": "9P7BP5VNWKX5"
}, },
"WPFAppxMicrosoft_WindowsDevHome": { "WPFAppxMicrosoft_WindowsDevHome": {
"Category": "Developer Tools", "Category": "Developer Tools",
"Content": "Dev Home", "Content": "Dev Home",
"Description": "Provides a specialized dashboard for software developer environment setups, repository syncing, and hardware widgets.", "Description": "Provides a specialized dashboard for software developer environment setups, repository syncing, and hardware widgets.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.Windows.DevHome" "PackageId": "Microsoft.Windows.DevHome",
"StoreId": "9N8MHTPHNGVV"
}, },
"WPFAppxMicrosoft_WindowsCrossDevice": { "WPFAppxMicrosoft_WindowsCrossDevice": {
"Category": "Microsoft Ecosystem", "Category": "Microsoft Ecosystem",
"Content": "Mobile Devices", "Content": "Mobile Devices",
"Description": "Manages system-level background connectivity with paired mobile devices. Removing this may disable cross-device features such as phone screen mirroring, file transfer, and mobile hotspot handoff integrated into Windows Settings.", "Description": "Manages system-level background connectivity with paired mobile devices. Removing this may disable cross-device features such as phone screen mirroring, file transfer, and mobile hotspot handoff integrated into Windows Settings.",
"Panel": "0", "Panel": "0",
"PackageId": "MicrosoftWindows.CrossDevice" "PackageId": "MicrosoftWindows.CrossDevice",
"StoreId": "9NTXGKQ8P7N0"
}, },
"WPFAppxMicrosoft_Todos": { "WPFAppxMicrosoft_Todos": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "To Do", "Content": "To Do",
"Description": "Creates, tracks, and synchronizes personal tasks, smart lists, and daily reminders.", "Description": "Creates, tracks, and synchronizes personal tasks, smart lists, and daily reminders.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.Todos" "PackageId": "Microsoft.Todos",
"StoreId": "9NBLGGH5R558"
}, },
"WPFAppxMicrosoft_PowerAutomateDesktop": { "WPFAppxMicrosoft_PowerAutomateDesktop": {
"Category": "Developer Tools", "Category": "Developer Tools",
"Content": "Power Automate", "Content": "Power Automate",
"Description": "Automates repetitive workflows and desktop tasks using low-code visual scripting.", "Description": "Automates repetitive workflows and desktop tasks using low-code visual scripting.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.PowerAutomateDesktop" "PackageId": "Microsoft.PowerAutomateDesktop",
"StoreId": "9NFTCH6J7FHV"
}, },
"WPFAppxMicrosoft_YourPhone": { "WPFAppxMicrosoft_YourPhone": {
"Category": "Microsoft Ecosystem", "Category": "Microsoft Ecosystem",
"Content": "Phone Link", "Content": "Phone Link",
"Description": "Synchronizes text messages, phone notifications, photos, and calls from a mobile device to the desktop.", "Description": "Synchronizes text messages, phone notifications, photos, and calls from a mobile device to the desktop.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.YourPhone" "PackageId": "Microsoft.YourPhone",
"StoreId": "9NMPJ99VJBWV"
}, },
"WPFAppxMicrosoft_MicrosoftStickyNotes": { "WPFAppxMicrosoft_MicrosoftStickyNotes": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Sticky Notes", "Content": "Sticky Notes",
"Description": "Creates quick, floating text notes on the desktop that automatically sync across devices.", "Description": "Creates quick, floating text notes on the desktop that automatically sync across devices.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.MicrosoftStickyNotes" "PackageId": "Microsoft.MicrosoftStickyNotes",
"StoreId": "9NBLGGH4QGHW"
}, },
"WPFAppxMicrosoft_WindowsSoundRecorder": { "WPFAppxMicrosoft_WindowsSoundRecorder": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Sound Recorder", "Content": "Sound Recorder",
"Description": "Records and trims live audio inputs with simple microphone adjustment controls.", "Description": "Records and trims live audio inputs with simple microphone adjustment controls.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.WindowsSoundRecorder" "PackageId": "Microsoft.WindowsSoundRecorder",
"StoreId": "9WZDNCRFHWKN"
}, },
"WPFAppxMicrosoft_WindowsAlarms": { "WPFAppxMicrosoft_WindowsAlarms": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Clock", "Content": "Clock",
"Description": "Features world clocks, alarms, countdown timers, stopwatches, and dedicated focus session tracking.", "Description": "Features world clocks, alarms, countdown timers, stopwatches, and dedicated focus session tracking.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.WindowsAlarms" "PackageId": "Microsoft.WindowsAlarms",
"StoreId": "9WZDNCRFJ3PR"
}, },
"WPFAppxMicrosoft_Paint": { "WPFAppxMicrosoft_Paint": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Paint", "Content": "Paint",
"Description": "Provides built-in digital sketching, basic image editing, and pixel-level graphic manipulation tools.", "Description": "Provides built-in digital sketching, basic image editing, and pixel-level graphic manipulation tools.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.Paint" "PackageId": "Microsoft.Paint",
"StoreId": "9PCFS5B6T72H"
}, },
"WPFAppxMicrosoft_WindowsNotepad": { "WPFAppxMicrosoft_WindowsNotepad": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Notepad", "Content": "Notepad",
"Description": "Provides a lightweight text editor with multi-tab support for plain text files and code snippets.", "Description": "Provides a lightweight text editor with multi-tab support for plain text files and code snippets.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.WindowsNotepad" "PackageId": "Microsoft.WindowsNotepad",
"StoreId": "9MSMLRH6LZF3"
}, },
"WPFAppxMicrosoft_ScreenSketch": { "WPFAppxMicrosoft_ScreenSketch": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Snipping Tool", "Content": "Snipping Tool",
"Description": "Captures screenshots or screen recordings with built-in markup, image cropping, and optical character recognition (OCR).", "Description": "Captures screenshots or screen recordings with built-in markup, image cropping, and optical character recognition (OCR).",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.ScreenSketch" "PackageId": "Microsoft.ScreenSketch",
"StoreId": "9MZ95KL8MR0L"
}, },
"WPFAppxMicrosoft_Copilot": { "WPFAppxMicrosoft_Copilot": {
"Category": "Bing & Web Services", "Category": "Bing & Web Services",
"Content": "Copilot", "Content": "Copilot",
"Description": "Launches the Microsoft AI companion for contextual answers, creative writing assistance, and intelligent web search.", "Description": "Launches the Microsoft AI companion for contextual answers, creative writing assistance, and intelligent web search.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.Copilot" "PackageId": "Microsoft.Copilot",
"StoreId": "9NHT9RB2F4HD"
}, },
"WPFAppxMicrosoft_WindowsCalculator": { "WPFAppxMicrosoft_WindowsCalculator": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Calculator", "Content": "Calculator",
"Description": "Performs standard arithmetic, scientific operations, programming calculations, and unit conversions.", "Description": "Performs standard arithmetic, scientific operations, programming calculations, and unit conversions.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.WindowsCalculator" "PackageId": "Microsoft.WindowsCalculator",
"StoreId": "9WZDNCRFHVN5"
}, },
"WPFAppxMicrosoft_WindowsCamera": { "WPFAppxMicrosoft_WindowsCamera": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Camera", "Content": "Camera",
"Description": "Captures photographs and records video files via connected webcams or imaging hardware.", "Description": "Captures photographs and records video files via connected webcams or imaging hardware.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.WindowsCamera" "PackageId": "Microsoft.WindowsCamera",
"StoreId": "9WZDNCRFJBBG"
}, },
"WPFAppxMicrosoft_WindowsPhotos": { "WPFAppxMicrosoft_WindowsPhotos": {
"Category": "Utilities & Productivity", "Category": "Utilities & Productivity",
"Content": "Photos", "Content": "Photos",
"Description": "Organizes, views, and crops local images with basic color adjustment and album creation tools.", "Description": "Organizes, views, and crops local images with basic color adjustment and album creation tools.",
"Panel": "0", "Panel": "0",
"PackageId": "Microsoft.Windows.Photos" "PackageId": "Microsoft.Windows.Photos",
"StoreId": "9WZDNCRFJBH4"
}, },
"WPFAppxMicrosoft_BingNews": { "WPFAppxMicrosoft_BingNews": {
"Category": "Bing & Web Services", "Category": "Bing & Web Services",
"Content": "News", "Content": "News",
"Description": "Aggregates breaking news headlines, personalized article feeds, and world current events.", "Description": "Aggregates breaking news headlines, personalized article feeds, and world current events.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.BingNews" "PackageId": "Microsoft.BingNews",
"StoreId": "9WZDNCRFHVFW"
}, },
"WPFAppxMicrosoft_BingWeather": { "WPFAppxMicrosoft_BingWeather": {
"Category": "Bing & Web Services", "Category": "Bing & Web Services",
"Content": "Weather", "Content": "Weather",
"Description": "Displays local real-time weather tracking, radar maps, and historical meteorological forecasts.", "Description": "Displays local real-time weather tracking, radar maps, and historical meteorological forecasts.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.BingWeather" "PackageId": "Microsoft.BingWeather",
"StoreId": "9WZDNCRFJ3Q2"
}, },
"WPFAppxMicrosoft_GamingApp": { "WPFAppxMicrosoft_GamingApp": {
"Category": "Xbox & Gaming", "Category": "Xbox & Gaming",
"Content": "Xbox App", "Content": "Xbox App",
"Description": "Serves as the primary gaming library manager, social community interface, and PC Game Pass dashboard.", "Description": "Serves as the primary gaming library manager, social community interface, and PC Game Pass dashboard.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.GamingApp" "PackageId": "Microsoft.GamingApp",
"StoreId": "9MV0B5HZVK9Z"
}, },
"WPFAppxMicrosoft_XboxGamingOverlay": { "WPFAppxMicrosoft_XboxGamingOverlay": {
"Category": "Xbox & Gaming", "Category": "Xbox & Gaming",
"Content": "Xbox Game Bar", "Content": "Xbox Game Bar",
"Description": "Provides customizable in-game status widgets, audio balancing sliders, system monitoring tools, and gameplay recording.", "Description": "Provides customizable in-game status widgets, audio balancing sliders, system monitoring tools, and gameplay recording.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.XboxGamingOverlay" "PackageId": "Microsoft.XboxGamingOverlay",
"StoreId": "9NZKPSTSNW4P"
}, },
"WPFAppxMicrosoft_XboxIdentityProvider": { "WPFAppxMicrosoft_XboxIdentityProvider": {
"Category": "Xbox & Gaming", "Category": "Xbox & Gaming",
"Content": "Xbox Identity Provider", "Content": "Xbox Identity Provider",
"Description": "Manages Xbox network user authentication and background account validation for connected titles. Warning: removing this may break Microsoft account sign-in for non-Xbox games and apps that rely on this authentication pipeline.", "Description": "Manages Xbox network user authentication and background account validation for connected titles. Warning: removing this may break Microsoft account sign-in for non-Xbox games and apps that rely on this authentication pipeline.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.XboxIdentityProvider" "PackageId": "Microsoft.XboxIdentityProvider",
"StoreId": "9WZDNCRD1HKW"
}, },
"WPFAppxMicrosoft_XboxSpeechToTextOverlay": { "WPFAppxMicrosoft_XboxSpeechToTextOverlay": {
"Category": "Xbox & Gaming", "Category": "Xbox & Gaming",
@@ -221,7 +250,8 @@
"Content": "Start Experiences App", "Content": "Start Experiences App",
"Description": "Powers the Windows Widgets board, delivering a personalized feed of news, weather, sports, and finance content.", "Description": "Powers the Windows Widgets board, delivering a personalized feed of news, weather, sports, and finance content.",
"Panel": "1", "Panel": "1",
"PackageId": "Microsoft.StartExperiencesApp" "PackageId": "Microsoft.StartExperiencesApp",
"StoreId": "9PC1H9VN18CM"
}, },
"WPFAppxMicrosoft_MicrosoftSolitaireCollection": { "WPFAppxMicrosoft_MicrosoftSolitaireCollection": {
"Category": "Xbox & Gaming", "Category": "Xbox & Gaming",
+52 -8
View File
@@ -179,6 +179,17 @@
"function": "Invoke-WPFFixesWinget", "function": "Invoke-WPFFixesWinget",
"link": "https://winutil.christitus.com/dev/features/fixes/winget" "link": "https://winutil.christitus.com/dev/features/fixes/winget"
}, },
"WPFPanelComputer": {
"Content": "Computer Management",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"compmgmt.msc"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer"
},
"WPFPanelControl": { "WPFPanelControl": {
"Content": "Control Panel", "Content": "Control Panel",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -190,16 +201,16 @@
], ],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/control" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/control"
}, },
"WPFPanelComputer": { "WPFPanelMouse": {
"Content": "Computer Management", "Content": "Mouse Properties",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [ "InvokeScript": [
"compmgmt.msc" "main.cpl"
], ],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/mouse"
}, },
"WPFPanelNetwork": { "WPFPanelNetwork": {
"Content": "Network Connections", "Content": "Network Connections",
@@ -234,6 +245,17 @@
], ],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/printer" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/printer"
}, },
"WPFPanelPrograms": {
"Content": "Programs and Features",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"appwiz.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/programs"
},
"WPFPanelRegion": { "WPFPanelRegion": {
"Content": "Region", "Content": "Region",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -245,16 +267,16 @@
], ],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/region" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/region"
}, },
"WPFPanelRestore": { "WPFPanelSecurity": {
"Content": "Windows Restore", "Content": "Security and Maintenance",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
"panel": "2", "panel": "2",
"Type": "Button", "Type": "Button",
"ButtonWidth": "300", "ButtonWidth": "300",
"InvokeScript": [ "InvokeScript": [
"rstrui.exe" "wscui.cpl"
], ],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/security"
}, },
"WPFPanelSound": { "WPFPanelSound": {
"Content": "Sound Settings", "Content": "Sound Settings",
@@ -289,6 +311,28 @@
], ],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/timedate" "link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/timedate"
}, },
"WPFPanelFirewall": {
"Content": "Windows Defender Firewall",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"firewall.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/firewall"
},
"WPFPanelRestore": {
"Content": "Windows Restore",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"rstrui.exe"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore"
},
"WPFWinUtilInstallPSProfile": { "WPFWinUtilInstallPSProfile": {
"Content": "CTT PowerShell Profile - Install", "Content": "CTT PowerShell Profile - Install",
"category": "Powershell Profile Powershell 7+ Only", "category": "Powershell Profile Powershell 7+ Only",
+7 -6
View File
@@ -1,9 +1,10 @@
{ {
"shared": { "shared": {
"AppEntryWidth": "200", "AppEntryWidth": "220",
"AppEntryFontSize": "11", "AppEntryFontSize": "13.2",
"AppEntryMargin": "1,0,1,0", "AppEntryIconSize": "28",
"AppEntryBorderThickness": "0", "AppEntryMargin": "3",
"AppEntryBorderThickness": "1",
"CustomDialogFontSize": "12", "CustomDialogFontSize": "12",
"CustomDialogFontSizeHeader": "14", "CustomDialogFontSizeHeader": "14",
"CustomDialogLogoSize": "25", "CustomDialogLogoSize": "25",
@@ -24,7 +25,7 @@
"IconFontSize": "14", "IconFontSize": "14",
"IconButtonSize": "35", "IconButtonSize": "35",
"SettingsIconFontSize": "18", "SettingsIconFontSize": "18",
"CloseIconFontSize": "18", "CloseIconFontSize": "12",
"GroupBorderBackgroundColor": "#232629", "GroupBorderBackgroundColor": "#232629",
"ButtonFontSize": "12", "ButtonFontSize": "12",
"ButtonFontFamily": "Arial", "ButtonFontFamily": "Arial",
@@ -99,7 +100,7 @@
"ScrollBarBackgroundColor": "#2E3135", "ScrollBarBackgroundColor": "#2E3135",
"ScrollBarHoverColor": "#3B4252", "ScrollBarHoverColor": "#3B4252",
"ScrollBarDraggingColor": "#5E81AC", "ScrollBarDraggingColor": "#5E81AC",
"ProgressBarForegroundColor": "#222222", "ProgressBarForegroundColor": "#6EFF72",
"ProgressBarBackgroundColor": "Transparent", "ProgressBarBackgroundColor": "Transparent",
"ProgressBarTextColor": "#232629", "ProgressBarTextColor": "#232629",
"ButtonInstallBackgroundColor": "#222222", "ButtonInstallBackgroundColor": "#222222",
+17 -1
View File
@@ -429,7 +429,7 @@
}, },
"WPFTweaksConsumerFeatures": { "WPFTweaksConsumerFeatures": {
"Content": "ConsumerFeatures - Disable", "Content": "ConsumerFeatures - Disable",
"Description": "Windows will not automatically install any games, third-party apps, or application links from the Windows Store for the signed-in user. Some default Apps will be inaccessible (e.g. Phone Link).", "Description": "Stops promoted app installs and reduces app suggestions from Microsoft Store content.",
"category": "Essential Tweaks", "category": "Essential Tweaks",
"panel": "1", "panel": "1",
"registry": [ "registry": [
@@ -944,6 +944,22 @@
], ],
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/wpbt" "link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/wpbt"
}, },
"WPFTweaksPreventDeviceMetadataFromNetwork": {
"Content": "Prevent Device Companion Apps",
"Description": "Prevents additional software from being installed when plugging in devices (e.g. Ads when plugging in a monitor). Poses potential security risk.",
"category": "Essential Tweaks",
"panel": "1",
"registry": [
{
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Device Metadata",
"Name": "PreventDeviceMetadataFromNetwork",
"Value": "1",
"Type": "DWord",
"OriginalValue": "<RemoveEntry>"
}
],
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/preventdevicemetadatafromnetwork"
},
"WPFTweaksRazerBlock": { "WPFTweaksRazerBlock": {
"Content": "Razer Software Auto-Install - Disable", "Content": "Razer Software Auto-Install - Disable",
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.", "Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
+1 -1
View File
@@ -5,7 +5,7 @@ toc: false
--- ---
> [!NOTE] > [!NOTE]
> This section contains technical documentation for developers. For end-user documentation, see the [User Guide](../userguide/). > This section contains code-only references. For end-user documentation, see the [User Guide](../userguide/).
## Architecture ## Architecture
@@ -3,7 +3,7 @@ title: "Computer Management"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=193} ```json {filename="config/feature.json",linenos=inline,linenostart=182}
"WPFPanelComputer": { "WPFPanelComputer": {
"Content": "Computer Management", "Content": "Computer Management",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "Control Panel"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=182} ```json {filename="config/feature.json",linenos=inline,linenostart=193}
"WPFPanelControl": { "WPFPanelControl": {
"Content": "Control Panel", "Content": "Control Panel",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -0,0 +1,16 @@
---
title: "Windows Defender Firewall"
description: ""
---
```json {filename="config/feature.json",linenos=inline,linenostart=314}
"WPFPanelFirewall": {
"Content": "Windows Defender Firewall",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"firewall.cpl"
],
```
@@ -0,0 +1,16 @@
---
title: "Mouse Properties"
description: ""
---
```json {filename="config/feature.json",linenos=inline,linenostart=204}
"WPFPanelMouse": {
"Content": "Mouse Properties",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"main.cpl"
],
```
@@ -3,7 +3,7 @@ title: "Network Connections"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=204} ```json {filename="config/feature.json",linenos=inline,linenostart=215}
"WPFPanelNetwork": { "WPFPanelNetwork": {
"Content": "Network Connections", "Content": "Network Connections",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "Power Panel"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=215} ```json {filename="config/feature.json",linenos=inline,linenostart=226}
"WPFPanelPower": { "WPFPanelPower": {
"Content": "Power Panel", "Content": "Power Panel",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "Printer Panel"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=226} ```json {filename="config/feature.json",linenos=inline,linenostart=237}
"WPFPanelPrinter": { "WPFPanelPrinter": {
"Content": "Printer Panel", "Content": "Printer Panel",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -0,0 +1,16 @@
---
title: "Programs and Features"
description: ""
---
```json {filename="config/feature.json",linenos=inline,linenostart=248}
"WPFPanelPrograms": {
"Content": "Programs and Features",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"appwiz.cpl"
],
```
@@ -3,7 +3,7 @@ title: "Region"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=237} ```json {filename="config/feature.json",linenos=inline,linenostart=259}
"WPFPanelRegion": { "WPFPanelRegion": {
"Content": "Region", "Content": "Region",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "Windows Restore"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=248} ```json {filename="config/feature.json",linenos=inline,linenostart=325}
"WPFPanelRestore": { "WPFPanelRestore": {
"Content": "Windows Restore", "Content": "Windows Restore",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -0,0 +1,16 @@
---
title: "Security and Maintenance"
description: ""
---
```json {filename="config/feature.json",linenos=inline,linenostart=270}
"WPFPanelSecurity": {
"Content": "Security and Maintenance",
"category": "Legacy Windows Panels",
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"wscui.cpl"
],
```
@@ -3,7 +3,7 @@ title: "Sound Settings"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=259} ```json {filename="config/feature.json",linenos=inline,linenostart=281}
"WPFPanelSound": { "WPFPanelSound": {
"Content": "Sound Settings", "Content": "Sound Settings",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "System Properties"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=270} ```json {filename="config/feature.json",linenos=inline,linenostart=292}
"WPFPanelSystem": { "WPFPanelSystem": {
"Content": "System Properties", "Content": "System Properties",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "Time and Date"
description: "" description: ""
--- ---
```json {filename="config/feature.json",linenos=inline,linenostart=281} ```json {filename="config/feature.json",linenos=inline,linenostart=303}
"WPFPanelTimedate": { "WPFPanelTimedate": {
"Content": "Time and Date", "Content": "Time and Date",
"category": "Legacy Windows Panels", "category": "Legacy Windows Panels",
@@ -3,7 +3,7 @@ title: "System Tray Battery Percentage"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1252} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1268}
"WPFToggleBatteryPercentage": { "WPFToggleBatteryPercentage": {
"Content": "System Tray Battery Percentage", "Content": "System Tray Battery Percentage",
"Description": "Shows numeric battery percentage next to the battery icon in the system tray.", "Description": "Shows numeric battery percentage next to the battery icon in the system tray.",
@@ -3,7 +3,7 @@ title: "Start Menu Bing Search"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1605} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1621}
"WPFToggleBingSearch": { "WPFToggleBingSearch": {
"Content": "Start Menu Bing Search", "Content": "Start Menu Bing Search",
"Description": "Toggles Bing web search results in Windows Search.", "Description": "Toggles Bing web search results in Windows Search.",
@@ -3,7 +3,7 @@ title: "Dark Theme for Windows"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1270} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1286}
"WPFToggleDarkMode": { "WPFToggleDarkMode": {
"Content": "Dark Theme for Windows", "Content": "Dark Theme for Windows",
"Description": "Dark Mode for the system and applications.", "Description": "Dark Mode for the system and applications.",
@@ -3,7 +3,7 @@ title: "BSoD Verbose Mode"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1226} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1242}
"WPFToggleDetailedBSoD": { "WPFToggleDetailedBSoD": {
"Content": "BSoD Verbose Mode", "Content": "BSoD Verbose Mode",
"Description": "Gives more information when you blue screen.", "Description": "Gives more information when you blue screen.",
@@ -3,7 +3,7 @@ title: "Lock Screen - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1641} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1657}
"WPFTweaksDisableLockscreen": { "WPFTweaksDisableLockscreen": {
"Content": "Lock Screen - Disable", "Content": "Lock Screen - Disable",
"Description": "Skips the lock screen entirely and goes directly to the sign-in screen on boot and wake.", "Description": "Skips the lock screen entirely and goes directly to the sign-in screen on boot and wake.",
@@ -3,7 +3,7 @@ title: "Game Mode"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1784} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1800}
"WPFToggleGameMode": { "WPFToggleGameMode": {
"Content": "Game Mode", "Content": "Game Mode",
"Description": "Toggles Windows prioritizes gaming performance by allocating system resources to games.", "Description": "Toggles Windows prioritizes gaming performance by allocating system resources to games.",
@@ -3,7 +3,7 @@ title: "File Explorer Hidden Files"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1340} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1356}
"WPFToggleHiddenFiles": { "WPFToggleHiddenFiles": {
"Content": "File Explorer Hidden Files", "Content": "File Explorer Hidden Files",
"Description": "Reveals hidden files in Explorer.", "Description": "Reveals hidden files in Explorer.",
@@ -3,7 +3,7 @@ title: "Settings Home Page"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1587} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1603}
"WPFToggleHideSettingsHome": { "WPFToggleHideSettingsHome": {
"Content": "Settings Home Page", "Content": "Settings Home Page",
"Description": "Toggles the Home Page in the Windows Settings app.", "Description": "Toggles the Home Page in the Windows Settings app.",
@@ -3,7 +3,7 @@ title: "Logon Screen Acrylic Blur"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1623} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1639}
"WPFToggleLoginBlur": { "WPFToggleLoginBlur": {
"Content": "Logon Screen Acrylic Blur", "Content": "Logon Screen Acrylic Blur",
"Description": "Toggles the acrylic blur effect on login screen background.", "Description": "Toggles the acrylic blur effect on login screen background.",
@@ -3,7 +3,7 @@ title: "Enable Long Paths"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1810} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1826}
"WPFToggleLongPaths": { "WPFToggleLongPaths": {
"Content": "Enable Long Paths", "Content": "Enable Long Paths",
"Description": "Toggles support for file paths longer than 260 characters in Explorer.", "Description": "Toggles support for file paths longer than 260 characters in Explorer.",
@@ -3,7 +3,7 @@ title: "Mouse Acceleration"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1473} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1489}
"WPFToggleMouseAcceleration": { "WPFToggleMouseAcceleration": {
"Content": "Mouse Acceleration", "Content": "Mouse Acceleration",
"Description": "Makes it so Cursor movement is affected by the speed of your physical mouse movements.", "Description": "Makes it so Cursor movement is affected by the speed of your physical mouse movements.",
@@ -3,7 +3,7 @@ title: "Multiplane Overlay"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1447} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1463}
"WPFToggleMultiplaneOverlay": { "WPFToggleMultiplaneOverlay": {
"Content": "Multiplane Overlay", "Content": "Multiplane Overlay",
"Description": "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards.", "Description": "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards.",
@@ -3,7 +3,7 @@ title: "Microsoft Outlook New Version"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1386} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1402}
"WPFToggleNewOutlook": { "WPFToggleNewOutlook": {
"Content": "Microsoft Outlook New Version", "Content": "Microsoft Outlook New Version",
"Description": "This will ensures the classic Outlook application is used.", "Description": "This will ensures the classic Outlook application is used.",
@@ -3,7 +3,7 @@ title: "Num Lock on Startup"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1507} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1523}
"WPFToggleNumLock": { "WPFToggleNumLock": {
"Content": "Num Lock on Startup", "Content": "Num Lock on Startup",
"Description": "Toggle the Num Lock key state when your computer starts.", "Description": "Toggle the Num Lock key state when your computer starts.",
@@ -3,7 +3,7 @@ title: "S3 Sleep"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1569} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1585}
"WPFToggleS3Sleep": { "WPFToggleS3Sleep": {
"Content": "S3 Sleep", "Content": "S3 Sleep",
"Description": "Toggles between Modern Standby and S3 Sleep, which cuts off power to the CPU while continuing to refresh the memory.", "Description": "Toggles between Modern Standby and S3 Sleep, which cuts off power to the CPU while continuing to refresh the memory.",
@@ -3,7 +3,7 @@ title: "Scrollbars Always Visible"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1428} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1444}
"WPFToggleScrollbars": { "WPFToggleScrollbars": {
"Content": "Scrollbars Always Visible", "Content": "Scrollbars Always Visible",
"Description": "If enabled, scrollbars will always be visible. If disabled, Windows will automatically hide scrollbars when not in use.", "Description": "If enabled, scrollbars will always be visible. If disabled, Windows will automatically hide scrollbars when not in use.",
@@ -3,7 +3,7 @@ title: "File Explorer File Extensions"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1312} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1328}
"WPFToggleShowExt": { "WPFToggleShowExt": {
"Content": "File Explorer File Extensions", "Content": "File Explorer File Extensions",
"Description": "Shows .file extensions in Explorer (.exe, .png, etc.)", "Description": "Shows .file extensions in Explorer (.exe, .png, etc.)",
@@ -3,7 +3,7 @@ title: "S0 Sleep Network Connectivity"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1551} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1567}
"WPFToggleStandbyFix": { "WPFToggleStandbyFix": {
"Content": "S0 Sleep Network Connectivity", "Content": "S0 Sleep Network Connectivity",
"Description": "Toggles network connectivity during S0 Sleep which is low power idle in modern laptops.", "Description": "Toggles network connectivity during S0 Sleep which is low power idle in modern laptops.",
@@ -3,7 +3,7 @@ title: "Start Menu Recommendations"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1658} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1674}
"WPFToggleStartMenuRecommendations": { "WPFToggleStartMenuRecommendations": {
"Content": "Start Menu Recommendations", "Content": "Start Menu Recommendations",
"Description": "Toggles the recommendations section in the Start Menu. WARNING: This will also disable Windows Spotlight on your Lock Screen as a side effect.", "Description": "Toggles the recommendations section in the Start Menu. WARNING: This will also disable Windows Spotlight on your Lock Screen as a side effect.",
@@ -3,7 +3,7 @@ title: "Sticky Keys"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1702} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1718}
"WPFToggleStickyKeys": { "WPFToggleStickyKeys": {
"Content": "Sticky Keys", "Content": "Sticky Keys",
"Description": "Toggles the Sticky Keys, which activate when clicking shift rapidly.", "Description": "Toggles the Sticky Keys, which activate when clicking shift rapidly.",
@@ -3,7 +3,7 @@ title: "Taskbar Task View Icon"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1766} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1782}
"WPFToggleTaskView": { "WPFToggleTaskView": {
"Content": "Taskbar Task View Icon", "Content": "Taskbar Task View Icon",
"Description": "Toggles the Task View Button in the Taskbar.", "Description": "Toggles the Task View Button in the Taskbar.",
@@ -3,7 +3,7 @@ title: "Taskbar Centered Icons"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1720} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1736}
"WPFToggleTaskbarAlignment": { "WPFToggleTaskbarAlignment": {
"Content": "Taskbar Centered Icons", "Content": "Taskbar Centered Icons",
"Description": "Toggles the Taskbar alignment either to the left or center.", "Description": "Toggles the Taskbar alignment either to the left or center.",
@@ -3,7 +3,7 @@ title: "Taskbar Search Icon"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1748} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1764}
"WPFToggleTaskbarSearch": { "WPFToggleTaskbarSearch": {
"Content": "Taskbar Search Icon", "Content": "Taskbar Search Icon",
"Description": "Toggles the Search Button on the Taskbar.", "Description": "Toggles the Search Button on the Taskbar.",
@@ -3,7 +3,7 @@ title: "Logon Verbose Mode"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1368} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1384}
"WPFToggleVerboseLogon": { "WPFToggleVerboseLogon": {
"Content": "Logon Verbose Mode", "Content": "Logon Verbose Mode",
"Description": "Show detailed messages during startup/shutdown.", "Description": "Show detailed messages during startup/shutdown.",
@@ -3,7 +3,7 @@ title: "Window Snapping"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1533} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1549}
"WPFToggleWindowSnapping": { "WPFToggleWindowSnapping": {
"Content": "Window Snapping", "Content": "Window Snapping",
"Description": "Toggles the window snapping feature when dragging windows.", "Description": "Toggles the window snapping feature when dragging windows.",
@@ -6,7 +6,7 @@ description: ""
```json {filename="config/tweaks.json",linenos=inline,linenostart=430} ```json {filename="config/tweaks.json",linenos=inline,linenostart=430}
"WPFTweaksConsumerFeatures": { "WPFTweaksConsumerFeatures": {
"Content": "ConsumerFeatures - Disable", "Content": "ConsumerFeatures - Disable",
"Description": "Windows will not automatically install any games, third-party apps, or application links from the Windows Store for the signed-in user. Some default Apps will be inaccessible (e.g. Phone Link).", "Description": "Stops promoted app installs and reduces app suggestions from Microsoft Store content.",
"category": "Essential Tweaks", "category": "Essential Tweaks",
"panel": "1", "panel": "1",
"registry": [ "registry": [
@@ -3,7 +3,7 @@ title: "Temporary Files - Remove"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1066} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1082}
"WPFTweaksDeleteTempFiles": { "WPFTweaksDeleteTempFiles": {
"Content": "Temporary Files - Remove", "Content": "Temporary Files - Remove",
"Description": "Erases TEMP Folders.", "Description": "Erases TEMP Folders.",
@@ -3,7 +3,7 @@ title: "File Explorer Automatic Folder Discovery - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1171} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1187}
"WPFTweaksDisableExplorerAutoDiscovery": { "WPFTweaksDisableExplorerAutoDiscovery": {
"Content": "File Explorer Automatic Folder Discovery - Disable", "Content": "File Explorer Automatic Folder Discovery - Disable",
"Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience. WARNING! Will disable File Explorer grouping.", "Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience. WARNING! Will disable File Explorer grouping.",
@@ -3,7 +3,7 @@ title: "Disk Cleanup - Run"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1053} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1069}
"WPFTweaksDiskCleanup": { "WPFTweaksDiskCleanup": {
"Content": "Disk Cleanup - Run", "Content": "Disk Cleanup - Run",
"Description": "Runs Disk Cleanup on Drive C: and removes old Windows Updates.", "Description": "Runs Disk Cleanup on Drive C: and removes old Windows Updates.",
@@ -0,0 +1,27 @@
---
title: "Prevent Device Companion Apps"
description: ""
---
```json {filename="config/tweaks.json",linenos=inline,linenostart=947}
"WPFTweaksPreventDeviceMetadataFromNetwork": {
"Content": "Prevent Device Companion Apps",
"Description": "Prevents additional software from being installed when plugging in devices (e.g. Ads when plugging in a monitor). Poses potential security risk.",
"category": "Essential Tweaks",
"panel": "1",
"registry": [
{
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Device Metadata",
"Name": "PreventDeviceMetadataFromNetwork",
"Value": "1",
"Type": "DWord",
"OriginalValue": "<RemoveEntry>"
}
],
```
## 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.
You can find information about the registry on [Wikipedia](https://en.wikipedia.org/wiki/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).
@@ -3,7 +3,7 @@ title: "Adobe URL Block List - Enable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1011} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1027}
"WPFTweaksBlockAdobeNet": { "WPFTweaksBlockAdobeNet": {
"Content": "Adobe URL Block List - Enable", "Content": "Adobe URL Block List - Enable",
"Description": "Reduces user interruptions by selectively blocking connections to Adobe's activation and telemetry servers. Credit: Ruddernation-Designs", "Description": "Reduces user interruptions by selectively blocking connections to Adobe's activation and telemetry servers. Credit: Ruddernation-Designs",
@@ -3,7 +3,7 @@ title: "Background Apps - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1139} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1155}
"WPFTweaksDisableBGapps": { "WPFTweaksDisableBGapps": {
"Content": "Background Apps - Disable", "Content": "Background Apps - Disable",
"Description": "Disables all Microsoft Store apps from running in the background, which has to be done individually since Windows 11.", "Description": "Disables all Microsoft Store apps from running in the background, which has to be done individually since Windows 11.",
@@ -3,7 +3,7 @@ title: "Fullscreen Optimizations - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1155} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1171}
"WPFTweaksDisableFSO": { "WPFTweaksDisableFSO": {
"Content": "Fullscreen Optimizations - Disable", "Content": "Fullscreen Optimizations - Disable",
"Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen.", "Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen.",
@@ -3,7 +3,7 @@ title: "IPv6 - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1117} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1133}
"WPFTweaksDisableIPv6": { "WPFTweaksDisableIPv6": {
"Content": "IPv6 - Disable", "Content": "IPv6 - Disable",
"Description": "Disables IPv6.", "Description": "Disables IPv6.",
@@ -3,7 +3,7 @@ title: "System Tray Notifications & Calendar - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=988} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1004}
"WPFTweaksDisableNotifications": { "WPFTweaksDisableNotifications": {
"Content": "System Tray Notifications & Calendar - Disable", "Content": "System Tray Notifications & Calendar - Disable",
"Description": "Disables all Notifications INCLUDING Calendar.", "Description": "Disables all Notifications INCLUDING Calendar.",
@@ -3,7 +3,7 @@ title: "IPv6 - Set IPv4 as Preferred"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1079} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1095}
"WPFTweaksIPv46": { "WPFTweaksIPv46": {
"Content": "IPv6 - Set IPv4 as Preferred", "Content": "IPv6 - Set IPv4 as Preferred",
"Description": "Setting the IPv4 preference can have latency and security benefits on private networks where IPv6 is not configured.", "Description": "Setting the IPv4 preference can have latency and security benefits on private networks where IPv6 is not configured.",
@@ -3,7 +3,7 @@ title: "Razer Software Auto-Install - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=947} ```json {filename="config/tweaks.json",linenos=inline,linenostart=963}
"WPFTweaksRazerBlock": { "WPFTweaksRazerBlock": {
"Content": "Razer Software Auto-Install - Disable", "Content": "Razer Software Auto-Install - Disable",
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.", "Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
@@ -3,7 +3,7 @@ title: "Right-Click Menu Previous Layout - Enable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1037} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1053}
"WPFTweaksRightClickMenu": { "WPFTweaksRightClickMenu": {
"Content": "Right-Click Menu Previous Layout - Enable", "Content": "Right-Click Menu Previous Layout - Enable",
"Description": "Restores the classic context menu when right-clicking in File Explorer, replacing the simplified Windows 11 version.", "Description": "Restores the classic context menu when right-clicking in File Explorer, replacing the simplified Windows 11 version.",
@@ -3,7 +3,7 @@ title: "Teredo - Disable"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1095} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1111}
"WPFTweaksTeredo": { "WPFTweaksTeredo": {
"Content": "Teredo - Disable", "Content": "Teredo - Disable",
"Description": "Teredo network tunneling is an IPv6 feature that can cause additional latency, but may cause problems with some games.", "Description": "Teredo network tunneling is an IPv6 feature that can cause additional latency, but may cause problems with some games.",
@@ -3,7 +3,7 @@ title: "DNS - Set to:"
description: "" description: ""
--- ---
```json {filename="config/tweaks.json",linenos=inline,linenostart=1835} ```json {filename="config/tweaks.json",linenos=inline,linenostart=1851}
"WPFchangedns": { "WPFchangedns": {
"Content": "DNS - Set to:", "Content": "DNS - Set to:",
"category": "z__Advanced Tweaks - CAUTION", "category": "z__Advanced Tweaks - CAUTION",
+8
View File
@@ -34,6 +34,14 @@ Use the quick-selection buttons at the top of the Tweaks tab to speed up setup:
* **Select Tweaks to Remove**: Choose the tweaks you want to disable or remove. * **Select Tweaks to Remove**: Choose the tweaks you want to disable or remove.
* **Undo Tweaks**: Click **Undo Selected Tweaks** at the bottom of the screen to apply the changes. * **Undo Tweaks**: Click **Undo Selected Tweaks** at the bottom of the screen to apply the changes.
### AppX Packages
Open **AppX Removal** from the Tweaks tab to manage the listed Windows apps. Select one or more packages, then choose **Install Selected** or **Remove Selected**.
When installing a selected package, WinUtil first registers an existing local `AppxManifest.xml`. If no usable local manifest remains, WinUtil installs the package from the Microsoft Store through WinGet when a Store product ID is available.
During AppX installation or removal, the window-level progress bar shows the current package, completed package count, and overall progress. The Windows taskbar also reflects progress and the final success or failure state.
### Essential Tweaks ### Essential Tweaks
Essential Tweaks are the safest starting point for most systems. They focus on lower-risk changes that improve usability, reduce noise, and avoid the more invasive changes found in advanced options. Essential Tweaks are the safest starting point for most systems. They focus on lower-risk changes that improve usability, reduce noise, and avoid the more invasive changes found in advanced options.
@@ -10,6 +10,9 @@ function Find-AppsByNameOrDescription {
.PARAMETER SearchString .PARAMETER SearchString
The string to be searched for. Wildcards are treated as literal characters. The string to be searched for. Wildcards are treated as literal characters.
.PARAMETER Category
When provided, only applications in this exact category are shown.
.NOTES .NOTES
- Uses module-scope $sync (no parameter needed; inherits from caller's scope) - Uses module-scope $sync (no parameter needed; inherits from caller's scope)
- Performs literal matching (no wildcard expansion) - Performs literal matching (no wildcard expansion)
@@ -18,7 +21,10 @@ function Find-AppsByNameOrDescription {
#> #>
param( param(
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[string]$SearchString = "" [string]$SearchString = "",
[Parameter(Mandatory = $false)]
[string]$Category = ""
) )
# Validate that $sync exists and has required structure # Validate that $sync exists and has required structure
@@ -39,7 +45,7 @@ function Find-AppsByNameOrDescription {
try { try {
# Reset the visibility if the search string is empty or the search is cleared # Reset the visibility if the search string is empty or the search is cleared
if ([string]::IsNullOrWhiteSpace($SearchString)) { if ([string]::IsNullOrWhiteSpace($SearchString) -and [string]::IsNullOrWhiteSpace($Category)) {
$sync.ItemsControl.Items | ForEach-Object { $sync.ItemsControl.Items | ForEach-Object {
# Each item is a StackPanel container # Each item is a StackPanel container
$_.Visibility = [Windows.Visibility]::Visible $_.Visibility = [Windows.Visibility]::Visible
@@ -94,10 +100,11 @@ function Find-AppsByNameOrDescription {
# Check if app matches search criteria # Check if app matches search criteria
if ($null -ne $appEntry) { if ($null -ne $appEntry) {
$contentMatch = $appEntry.Content -like "*$escapedSearchString*" $categoryMatch = -not [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Category -eq $Category
$descriptionMatch = $appEntry.Description -like "*$escapedSearchString*" $contentMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Content -like "*$escapedSearchString*"
$descriptionMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Description -like "*$escapedSearchString*"
if ($contentMatch -or $descriptionMatch) { if ($categoryMatch -or $contentMatch -or $descriptionMatch) {
# Show the App and mark that this category has a match # Show the App and mark that this category has a match
$appControl.Visibility = [Windows.Visibility]::Visible $appControl.Visibility = [Windows.Visibility]::Visible
$categoryHasMatch = $true $categoryHasMatch = $true
@@ -0,0 +1,24 @@
function Get-WinUtilInstalledAPPX {
<#
.SYNOPSIS
Gets the names of AppX packages installed for all users
#>
# AppX module auto-loading can leave PowerShell 7 dependent on a temporary Windows PowerShell
# compatibility proxy. Run the query in Windows PowerShell 5.1 so it remains available after
# those temporary proxy files are removed.
$ps5Command = {
Get-AppxPackage -AllUsers -ErrorAction Stop | Select-Object -ExpandProperty Name
}
$packageOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command 2>&1
if ($LASTEXITCODE -ne 0) {
$failureDetails = ($packageOutput | Out-String).Trim()
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "Failed to get installed AppX packages: $failureDetails"
return @()
}
return @($packageOutput)
}
@@ -60,6 +60,30 @@ function Initialize-InstallAppEntry {
$borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor") $borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor")
}) })
$contentPanel = New-Object Windows.Controls.StackPanel
$contentPanel.Orientation = "Horizontal"
$contentPanel.VerticalAlignment = [Windows.VerticalAlignment]::Center
$icon = New-Object Windows.Controls.Grid
$icon.SetResourceReference([Windows.FrameworkElement]::WidthProperty, "AppEntryIconSize")
$icon.SetResourceReference([Windows.FrameworkElement]::HeightProperty, "AppEntryIconSize")
$icon.Margin = New-Object Windows.Thickness(0, 0, 8, 0)
$fallback = New-Object Windows.Controls.TextBlock
$fallback.Text = $app.content.TrimStart(".").Substring(0, 1).ToUpper()
$fallback.FontWeight = "Bold"; $fallback.HorizontalAlignment = "Center"; $fallback.VerticalAlignment = "Center"
if ($app.link) { $fallback.Visibility = "Collapsed" }
$fallback.SetResourceReference([Windows.Controls.TextBlock]::FontSizeProperty, "AppEntryFontSize")
$fallback.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "ToggleButtonOnColor")
[void]$icon.Children.Add($fallback)
if ($app.link) {
$logo = New-Object Windows.Controls.Image
$logo.Stretch = [Windows.Media.Stretch]::Uniform
$logo.Source = "https://www.google.com/s2/favicons?sz=64&domain_url=$([uri]::EscapeDataString($app.link))"
$logo.Add_ImageFailed({ $this.Visibility = "Collapsed"; $this.Parent.Children[0].Visibility = "Visible" })
[void]$icon.Children.Add($logo)
}
[void]$contentPanel.Children.Add($icon)
# Create the TextBlock for the application name # Create the TextBlock for the application name
$appName = New-Object Windows.Controls.TextBlock $appName = New-Object Windows.Controls.TextBlock
$appName.Style = $sync.Form.Resources.AppEntryNameStyle $appName.Style = $sync.Form.Resources.AppEntryNameStyle
@@ -73,7 +97,8 @@ function Initialize-InstallAppEntry {
[void]$appName.Inlines.Add($fossRun) [void]$appName.Inlines.Add($fossRun)
} }
$checkBox.Content = $appName [void]$contentPanel.Children.Add($appName)
$checkBox.Content = $contentPanel
# Add accessibility properties to make the elements screen reader friendly # Add accessibility properties to make the elements screen reader friendly
$checkBox.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $app.content) $checkBox.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $app.content)
+79
View File
@@ -0,0 +1,79 @@
function Install-WinUtilAPPX {
<#
.SYNOPSIS
Registers a local AppX package or installs it from the Microsoft Store
.PARAMETER Name
The AppX package name to install
.PARAMETER StoreId
The optional Microsoft Store product ID used when no local manifest is available
#>
param (
[Parameter(Mandatory = $true)]
[string]$Name,
[string]$StoreId
)
Write-WinUtilLog -Component "AppX" -Message "Installing AppX package: $Name"
# AppX and DISM cmdlets are more reliable in Windows PowerShell 5.1. Query both installed and
# provisioned package metadata because either can expose a local manifest that can be registered.
$ps5Command = {
$packageName = $args[0]
$manifestPaths = [System.Collections.Generic.List[string]]::new()
Get-AppxPackage -AllUsers -Name $packageName -ErrorAction SilentlyContinue |
Sort-Object -Property Version -Descending |
ForEach-Object {
if (-not [string]::IsNullOrWhiteSpace($_.InstallLocation)) {
$manifestPaths.Add((Join-Path $_.InstallLocation "AppxManifest.xml"))
}
}
Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
Where-Object DisplayName -EQ $packageName |
ForEach-Object {
if (-not [string]::IsNullOrWhiteSpace($_.InstallLocation)) {
$manifestPaths.Add((Join-Path $_.InstallLocation "AppxManifest.xml"))
}
}
$manifestPath = $manifestPaths |
Select-Object -Unique |
Where-Object { Test-Path -LiteralPath $_ } |
Select-Object -First 1
if ($null -ne $manifestPath) {
Add-AppxPackage -Register $manifestPath -DisableDevelopmentMode -ErrorAction Stop
Write-Output $manifestPath
}
}
$manifestOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $Name 2>&1
if ($LASTEXITCODE -eq 0 -and $null -ne $manifestOutput) {
$manifestPath = ($manifestOutput | Select-Object -Last 1).ToString().Trim()
if (-not [string]::IsNullOrWhiteSpace($manifestPath)) {
Write-WinUtilLog -Component "AppX" -Message "Registered local AppX manifest for $Name`: $manifestPath"
return
}
}
if ($LASTEXITCODE -ne 0) {
$failureDetails = ($manifestOutput | Out-String).Trim()
Write-WinUtilLog -Level "WARN" -Component "AppX" -Message "Local AppX registration failed for $Name`: $failureDetails"
}
if ([string]::IsNullOrWhiteSpace($StoreId)) {
$errorMessage = "Unable to install $Name because no local manifest or Microsoft Store ID is available."
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message $errorMessage
throw $errorMessage
}
Write-WinUtilLog -Component "AppX" -Message "No usable local manifest found for $Name. Installing Microsoft Store product $StoreId."
Install-WinUtilWinget
Install-WinUtilProgramWinget -Action Install -Programs @("msstore:$StoreId")
}
@@ -78,6 +78,7 @@ function Invoke-WinUtilTweaks {
$sync.configs.tweaks.$CheckBox.appx | ForEach-Object { $sync.configs.tweaks.$CheckBox.appx | ForEach-Object {
Remove-WinUtilAPPX -Name $psitem Remove-WinUtilAPPX -Name $psitem
} }
Remove-WinUtilProvisionedAPPX -PackageList $sync.configs.tweaks.$CheckBox.appx
} }
} }
Write-WinUtilLog -Component "Tweaks" -Message "$action tweak completed: $CheckBox" Write-WinUtilLog -Component "Tweaks" -Message "$action tweak completed: $CheckBox"
+15 -2
View File
@@ -17,7 +17,20 @@ function Remove-WinUtilAPPX {
Write-Host "Removing $Name" Write-Host "Removing $Name"
Write-WinUtilLog -Component "AppX" -Message "Removing AppX package pattern: $Name" Write-WinUtilLog -Component "AppX" -Message "Removing AppX package pattern: $Name"
Get-AppxPackage $Name -AllUsers | Remove-AppxPackage -AllUsers
Get-AppxProvisionedPackage -Online | Where-Object DisplayName -like $Name | Remove-AppxProvisionedPackage -Online # We explicitly loop through packages instead of using the pipeline because PowerShell 7 pipeline binding
# for Remove-AppxPackage fails silently, and Get-AppxPackage -AllUsers returns duplicate objects for each user profile.
$pkgs = Get-AppxPackage "*$Name*" -AllUsers | Sort-Object -Property PackageFullName -Unique
if ($null -ne $pkgs) {
foreach ($pkg in $pkgs) {
try {
Remove-AppxPackage -Package $pkg.PackageFullName -AllUsers -ErrorAction Stop
}
catch {
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "Failed to remove AppX package $($pkg.PackageFullName): $($_.Exception.Message)"
}
}
}
Write-WinUtilLog -Component "AppX" -Message "AppX removal completed for package pattern: $Name" Write-WinUtilLog -Component "AppX" -Message "AppX removal completed for package pattern: $Name"
} }
@@ -0,0 +1,62 @@
function Remove-WinUtilProvisionedAPPX {
<#
.SYNOPSIS
Removes all AppX provisioned packages that match the given names
.PARAMETER PackageList
An array of names of the APPX packages to remove
.EXAMPLE
Remove-WinUtilProvisionedAPPX -PackageList @("Microsoft.Microsoft3DViewer", "Microsoft.WindowsCalculator")
#>
param (
[string[]]$PackageList
)
if ($null -eq $PackageList -or $PackageList.Count -eq 0) {
return
}
Write-Host "`nRemoving provisioned packages..."
Write-WinUtilLog -Component "AppX" -Message "Removing AppX provisioned packages: $($PackageList -join ', ')"
# DISM cmdlets like Get-AppxProvisionedPackage often fail with "Class not registered" or hang in PowerShell 7.
# We shell out to Windows PowerShell 5.1 (powershell.exe) to reliably remove the provisioned packages.
$ps5Command = {
$pkgs = $args
$provisionedPackages = Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue
$failures = [System.Collections.Generic.List[string]]::new()
foreach ($Package in $pkgs) {
$provs = $provisionedPackages |
Where-Object DisplayName -Like "*$Package*"
if ($null -ne $provs) {
foreach ($prov in $provs) {
try {
Remove-AppxProvisionedPackage -Online -PackageName $prov.PackageName -ErrorAction Stop | Out-Null
}
catch {
$failures.Add("Failed to remove provisioned AppX package $($prov.PackageName): $($_.Exception.Message)")
}
}
}
}
if ($failures.Count -gt 0) {
throw ($failures -join [Environment]::NewLine)
}
}
$removalOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $PackageList 2>&1
if ($LASTEXITCODE -ne 0 -or $null -ne $removalOutput) {
$failureDetails = ($removalOutput | Out-String).Trim()
$errorMessage = "AppX provisioned package removal failed: $failureDetails"
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message $errorMessage
throw $errorMessage
}
Write-WinUtilLog -Component "AppX" -Message "AppX provisioned package removal completed."
}
@@ -0,0 +1,17 @@
function Set-WinUtilAppCategoryFilter {
<#
.SYNOPSIS
Applies an exact application category filter from an Install tab search chip.
.PARAMETER Category
The application category to show. An empty value clears the filter.
#>
param(
[Parameter(Mandatory = $false)]
[string]$Category = ""
)
$sync.SearchBar.Tag = $Category
$sync.SearchBar.Text = $Category
Find-AppsByNameOrDescription -SearchString $Category -Category $Category
}
@@ -0,0 +1,35 @@
function Set-WinUtilTweaksProgressIndicator {
<#
.SYNOPSIS
Shows, updates, or hides the window-level progress indicator used by long-running
workflows such as Tweaks, Undo, and AppX management. It lives outside the TabControl,
so unlike the Install tab's progress bar it stays visible no matter which tab is active.
.PARAMETER Visible
Whether the indicator should be shown or hidden.
.PARAMETER Label
The text to display above the progress bar.
.PARAMETER Percent
The percentage of the progress bar that should be filled (0-100).
#>
param(
[bool]$Visible,
[string]$Label,
[ValidateRange(0,100)]
[int]$Percent
)
$indicatorVisible = if ($Visible) { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed }
$indicatorLabel = $Label
$hasLabel = $PSBoundParameters.ContainsKey('Label')
$hasPercent = $PSBoundParameters.ContainsKey('Percent')
Invoke-WPFUIThread -ScriptBlock {
$sync.WPFTweaksProgressBar.Visibility = $indicatorVisible
if ($hasLabel) {
$sync.WPFTweaksProgressLabel.Text = $indicatorLabel
}
if ($hasPercent) {
$sync.WPFTweaksProgressValue.Value = $Percent
}
}
}
@@ -9,7 +9,7 @@ function Invoke-WinUtilInstallAppRenderBatch {
} }
if ($sync.currentTab -eq "Install" -and $sync.SearchBar -and -not [string]::IsNullOrWhiteSpace($sync.SearchBar.Text)) { if ($sync.currentTab -eq "Install" -and $sync.SearchBar -and -not [string]::IsNullOrWhiteSpace($sync.SearchBar.Text)) {
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Category $sync.SearchBar.Tag
} }
} }
@@ -0,0 +1,68 @@
function Invoke-WPFAppxInstall {
if ($sync.ProcessRunning) {
Show-WinUtilMessage -Message "An AppX process is currently running." -Title "WinUtil" -Button "OK" -Icon "Warning"
return
}
if ($null -eq $sync.selectedAppx -or $sync.selectedAppx.Count -eq 0) {
Show-WinUtilMessage -Message "No AppX Package selected" -Title "Error" -Button "OK" -Icon "Error"
return
}
$selected = @($sync.selectedAppx)
$apps = $sync.configs.appxHashtable
$sync.ProcessRunning = $true
Invoke-WPFRunspace -ParameterList @(("selected", $selected), ("apps", $apps)) -ScriptBlock {
param($selected, $apps)
$totalPackages = @($selected).Count
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
try {
Write-WinUtilLog -Component "AppX" -Message "Starting AppX install for $totalPackages selected package(s)."
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing AppX install (0/$totalPackages)" -Percent 0
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
}
for ($index = 0; $index -lt $totalPackages; $index++) {
$key = $selected[$index]
$app = $apps[$key]
$position = $index + 1
$startPercent = [int](($index / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installing $($app.Content) ($position/$totalPackages)" -Percent $startPercent
}
Write-Host "Installing $($app.Content)"
Install-WinUtilAPPX -Name $app.PackageId -StoreId $app.StoreId
$completedPercent = [int](($position / $totalPackages) * 100)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installed $($app.Content) ($position/$totalPackages)" -Percent $completedPercent
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
}
}
Write-Host "================================="
Write-Host "-- AppX Install Finished ---"
Write-Host "================================="
Write-WinUtilLog -Component "AppX" -Message "AppX install finished."
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "AppX install finished" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
}
}
catch {
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "AppX install failed: $($_.Exception.Message)"
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "AppX install failed" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
}
}
finally {
$sync.ProcessRunning = $false
}
}
}
+60 -11
View File
@@ -1,23 +1,45 @@
function Invoke-WPFAppxRemoval { function Invoke-WPFAppxRemoval {
if ($sync.ProcessRunning) {
Show-WinUtilMessage -Message "An AppX process is currently running." -Title "WinUtil" -Button "OK" -Icon "Warning"
return
}
if ($null -eq $sync.selectedAppx -or $sync.selectedAppx.Count -eq 0) { if ($null -eq $sync.selectedAppx -or $sync.selectedAppx.Count -eq 0) {
Show-WinUtilMessage -Message "No AppX Package selected" -Title "Error" -Button "OK" -Icon "Error" Show-WinUtilMessage -Message "No AppX Package selected" -Title "Error" -Button "OK" -Icon "Error"
return return
} }
$selected = $sync.selectedAppx $selected = @($sync.selectedAppx)
$apps = $sync.configs.appxHashtable $apps = $sync.configs.appxHashtable
$sync.ProcessRunning = $true
Invoke-WPFRunspace -ParameterList @(("selected", $selected), ("apps", $apps)) -ScriptBlock { Invoke-WPFRunspace -ParameterList @(("selected", $selected), ("apps", $apps)) -ScriptBlock {
param($selected, $apps) param($selected, $apps)
$sync.ProcessRunning = $true $totalPackages = @($selected).Count
Write-WinUtilLog -Component "AppX" -Message "Starting AppX removal for $(@($selected).Count) selected package(s)." $hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
$packageList = [System.Collections.Generic.List[string]]::new()
try {
Write-WinUtilLog -Component "AppX" -Message "Starting AppX removal for $totalPackages selected package(s)."
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing AppX removal (0/$totalPackages)" -Percent 0
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
}
for ($index = 0; $index -lt $totalPackages; $index++) {
$key = $selected[$index]
$app = $apps[$key]
$position = $index + 1
$startPercent = [int](($index / $totalPackages) * 90)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Removing $($app.Content) ($position/$totalPackages)" -Percent $startPercent
}
foreach ($key in $selected) {
if ($key -eq "WPFAppxMicrosoft_XboxGamingOverlay") { if ($key -eq "WPFAppxMicrosoft_XboxGamingOverlay") {
# Making sure Game Bar isn't running # Making sure Game Bar isn't running
Write-WinUtilLog -Component "AppX" -Message "Stopping GameBarFTServer before removing Xbox Gaming Overlay." Write-WinUtilLog -Component "AppX" -Message "Stopping GameBarFTServer before removing Xbox Gaming Overlay."
Stop-Process -Name GameBarFTServer Stop-Process -Name GameBarFTServer -Force -Confirm:$false -ErrorAction SilentlyContinue
# This stops annoying ms-gamebar popup when launching games. # This stops annoying ms-gamebar popup when launching games.
Write-WinUtilLog -Component "AppX" -Message "Disabling Game DVR capture before removing Xbox Gaming Overlay." Write-WinUtilLog -Component "AppX" -Message "Disabling Game DVR capture before removing Xbox Gaming Overlay."
@@ -25,27 +47,54 @@ function Invoke-WPFAppxRemoval {
} }
if ($key -eq "WPFAppxMicrosoft_WindowsNotepad") { if ($key -eq "WPFAppxMicrosoft_WindowsNotepad") {
# i hope your having fun reading this
Write-WinUtilLog -Component "AppX" -Message "Stopping dllhost before removing Notepad." Write-WinUtilLog -Component "AppX" -Message "Stopping dllhost before removing Notepad."
Stop-Process -Name dllhost Stop-Process -Name dllhost -Force -Confirm:$false -ErrorAction SilentlyContinue
} }
Write-Host "Removing $($apps[$key].Content)" Write-Host "Removing $($app.Content)"
Write-WinUtilLog -Component "AppX" -Message "Removing $($apps[$key].Content) ($($apps[$key].PackageId))." Write-WinUtilLog -Component "AppX" -Message "Removing $($app.Content) ($($app.PackageId))."
Get-AppxPackage -Name $apps[$key].PackageId -AllUsers | Remove-AppxPackage -AllUsers Remove-WinUtilAPPX -Name $app.PackageId
$packageList.Add($app.PackageId)
if ($key -eq "WPFAppxMSTeams") { if ($key -eq "WPFAppxMSTeams") {
# Uninstalls Microsoft Teams Meeting Add-in for Microsoft Office # Uninstalls Microsoft Teams Meeting Add-in for Microsoft Office
Write-WinUtilLog -Component "AppX" -Message "Uninstalling Microsoft Teams meeting add-in package." Write-WinUtilLog -Component "AppX" -Message "Uninstalling Microsoft Teams meeting add-in package."
Get-Package -Name "Microsoft Teams*" -ErrorAction SilentlyContinue | Uninstall-Package -Force Get-Package -Name "Microsoft Teams*" -ErrorAction SilentlyContinue | Uninstall-Package -Force
} }
$completedPercent = [int](($position / $totalPackages) * 90)
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Removed $($app.Content) ($position/$totalPackages)" -Percent $completedPercent
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
}
}
if ($packageList.Count -gt 0) {
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Removing provisioned AppX packages" -Percent 90
}
Remove-WinUtilProvisionedAPPX -PackageList $packageList.ToArray()
} }
Write-Host "=================================" Write-Host "================================="
Write-Host "-- AppX Removal Finished ---" Write-Host "-- AppX Removal Finished ---"
Write-Host "=================================" Write-Host "================================="
Write-WinUtilLog -Component "AppX" -Message "AppX removal finished." Write-WinUtilLog -Component "AppX" -Message "AppX removal finished."
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "AppX removal finished" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
}
}
catch {
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "AppX removal failed: $($_.Exception.Message)"
if ($hasUI) {
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "AppX removal failed" -Percent 100
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
}
}
finally {
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
} }
}
} }
+12 -1
View File
@@ -16,6 +16,7 @@ function Invoke-WPFButton {
#[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info") #[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info")
if (-not $sync.ProcessRunning) { if (-not $sync.ProcessRunning) {
Set-WinUtilProgressBar -label "" -percent 0 Set-WinUtilProgressBar -label "" -percent 0
Set-WinUtilTweaksProgressIndicator -Visible $false
} }
# Check if button is defined in feature config with function or InvokeScript # Check if button is defined in feature config with function or InvokeScript
@@ -65,6 +66,9 @@ 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"}
"WPFAppxRemoval" {Invoke-WPFTab "WPFTab6BT"}
"WPFBackToTweaks" {Invoke-WPFTab "WPFTab2BT"}
"WPFInstallSelectedAppx" {Invoke-WPFAppxInstall}
"WPFRemoveSelectedAppx" {Invoke-WPFAppxRemoval} "WPFRemoveSelectedAppx" {Invoke-WPFAppxRemoval}
"WPFDefaultAppxSelection" {Invoke-WPFPresets "AppxDefault" -checkboxfilterpattern "WPFAppx*"} "WPFDefaultAppxSelection" {Invoke-WPFPresets "AppxDefault" -checkboxfilterpattern "WPFAppx*"}
"WPFSelectAllAppx" { "WPFSelectAllAppx" {
@@ -74,7 +78,7 @@ function Invoke-WPFButton {
$sync.configs.appxHashtable.Keys | ForEach-Object {$sync.$_.IsChecked = $false} $sync.configs.appxHashtable.Keys | ForEach-Object {$sync.$_.IsChecked = $false}
} }
"WPFGetInstalledAppx" { "WPFGetInstalledAppx" {
$installedAppxPackages = Get-AppxPackage -AllUsers | Select-Object -ExpandProperty Name $installedAppxPackages = Get-WinUtilInstalledAPPX
foreach ($appx in $sync.configs.appxHashtable.GetEnumerator()) { foreach ($appx in $sync.configs.appxHashtable.GetEnumerator()) {
if ($appx.Value.PackageId -in $installedAppxPackages) { if ($appx.Value.PackageId -in $installedAppxPackages) {
$sync.$($appx.Key).IsChecked = $true $sync.$($appx.Key).IsChecked = $true
@@ -83,6 +87,13 @@ function Invoke-WPFButton {
} }
"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}
"WPFMaximizeButton" {
if ($sync.Form.WindowState -eq [Windows.WindowState]::Normal) {
$sync.Form.WindowState = [Windows.WindowState]::Maximized
} else {
$sync.Form.WindowState = [Windows.WindowState]::Normal
}
}
"WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen} "WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen}
} }
} }
+1 -2
View File
@@ -19,13 +19,12 @@ function Invoke-WPFTab {
$tabNumber = [int]($ClickedTab -replace "WPFTab","" -replace "BT","") - 1 $tabNumber = [int]($ClickedTab -replace "WPFTab","" -replace "BT","") - 1
$filter = Get-WinUtilVariables -Type ToggleButton | Where-Object {$psitem -like "WPFTab?BT"} $filter = Get-WinUtilVariables -Type ToggleButton | Where-Object {$psitem -like "WPFTab?BT"}
$sync.$tabNav.Items[$tabNumber].IsSelected = $true
($sync.GetEnumerator()).where{$psitem.Key -in $filter} | ForEach-Object { ($sync.GetEnumerator()).where{$psitem.Key -in $filter} | ForEach-Object {
if ($ClickedTab -ne $PSItem.name) { if ($ClickedTab -ne $PSItem.name) {
$sync[$PSItem.Name].IsChecked = $false $sync[$PSItem.Name].IsChecked = $false
} else { } else {
$sync["$ClickedTab"].IsChecked = $true $sync["$ClickedTab"].IsChecked = $true
$tabNumber = [int]($ClickedTab-replace "WPFTab","" -replace "BT","") - 1
$sync.$tabNav.Items[$tabNumber].IsSelected = $true
} }
} }
$sync.currentTab = $sync.$tabNav.Items[$tabNumber].Header $sync.currentTab = $sync.$tabNav.Items[$tabNumber].Header
+1 -1
View File
@@ -441,7 +441,7 @@ function Invoke-WPFUIElements {
$sync[$entryInfo.Name].Add_Unchecked({ $sync[$entryInfo.Name].Add_Unchecked({
[System.Object]$Sender = $args[0] [System.Object]$Sender = $args[0]
Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkbox $Sender.name Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkboxName $Sender.name
}) })
} }
} }
@@ -40,12 +40,14 @@ function Invoke-WPFtweaksbutton {
} }
Set-WinUtilProgressBar -Label "Creating restore point" -Percent 0 Set-WinUtilProgressBar -Label "Creating restore point" -Percent 0
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Creating restore point" -Percent 0
Write-WinUtilLog -Component "Tweaks" -Message "Creating restore point before applying selected tweaks." Write-WinUtilLog -Component "Tweaks" -Message "Creating restore point before applying selected tweaks."
Invoke-WinUtilTweaks $restorePointTweak Invoke-WinUtilTweaks $restorePointTweak
$completedSteps = 1 $completedSteps = 1
if ($tweaksToRun.Count -eq 0 -and $dnsProvider -eq "Default") { if ($tweaksToRun.Count -eq 0 -and $dnsProvider -eq "Default") {
Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100 Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "=================================" Write-Host "================================="
@@ -76,12 +78,14 @@ function Invoke-WPFtweaksbutton {
for ($i = 0; $i -lt $tweaks.Count; $i++) { for ($i = 0; $i -lt $tweaks.Count; $i++) {
Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($completedSteps / $totalSteps * 100) Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($completedSteps / $totalSteps * 100)
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Applying $($tweaks[$i]) ($($completedSteps + 1)/$totalSteps)" -Percent ($completedSteps / $totalSteps * 100)
Invoke-WinUtilTweaks $tweaks[$i] Invoke-WinUtilTweaks $tweaks[$i]
$completedSteps++ $completedSteps++
$progress = $completedSteps / $totalSteps $progress = $completedSteps / $totalSteps
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value $progress } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value $progress }
} }
Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100 Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Tweaks finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "=================================" Write-Host "================================="
+2
View File
@@ -34,11 +34,13 @@ function Invoke-WPFundoall {
for ($i = 0; $i -lt $tweaks.Count; $i++) { for ($i = 0; $i -lt $tweaks.Count; $i++) {
Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100) Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100)
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Undoing $($tweaks[$i]) ($($i + 1)/$($tweaks.Count))" -Percent ($i / $tweaks.Count * 100)
Invoke-WinUtiltweaks $tweaks[$i] -undo $true Invoke-WinUtiltweaks $tweaks[$i] -undo $true
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) }
} }
Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100 Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Undo Tweaks Finished" -Percent 100
$sync.ProcessRunning = $false $sync.ProcessRunning = $false
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" } Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "==================================" Write-Host "=================================="
+504 -20
View File
@@ -1,11 +1,38 @@
#=========================================================================== #===========================================================================
# Tests - AppX Removal # Tests - AppX Management
#=========================================================================== #===========================================================================
BeforeAll { BeforeAll {
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
. (Join-Path $script:repoRoot "functions\private\Get-WinUtilInstalledAPPX.ps1")
. (Join-Path $script:repoRoot "functions\private\Install-WinUtilAPPX.ps1")
. (Join-Path $script:repoRoot "functions\private\Remove-WinUtilAPPX.ps1") . (Join-Path $script:repoRoot "functions\private\Remove-WinUtilAPPX.ps1")
. (Join-Path $script:repoRoot "functions\private\Remove-WinUtilProvisionedAPPX.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFAppxInstall.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFAppxRemoval.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFAppxRemoval.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1")
$tokens = $null
$parseErrors = $null
$provisionedSourcePath = Join-Path $script:repoRoot "functions\private\Remove-WinUtilProvisionedAPPX.ps1"
$provisionedSourceAst = [System.Management.Automation.Language.Parser]::ParseFile($provisionedSourcePath, [ref]$tokens, [ref]$parseErrors)
$ps5CommandAssignment = $provisionedSourceAst.Find({
param($node)
$node -is [System.Management.Automation.Language.AssignmentStatementAst] -and
$node.Left -is [System.Management.Automation.Language.VariableExpressionAst] -and
$node.Left.VariablePath.UserPath -eq "ps5Command"
}, $true)
$script:provisionedRemovalScriptBlock = $ps5CommandAssignment.Right.Expression.ScriptBlock.GetScriptBlock()
$installSourcePath = Join-Path $script:repoRoot "functions\private\Install-WinUtilAPPX.ps1"
$installSourceAst = [System.Management.Automation.Language.Parser]::ParseFile($installSourcePath, [ref]$tokens, [ref]$parseErrors)
$installCommandAssignment = $installSourceAst.Find({
param($node)
$node -is [System.Management.Automation.Language.AssignmentStatementAst] -and
$node.Left -is [System.Management.Automation.Language.VariableExpressionAst] -and
$node.Left.VariablePath.UserPath -eq "ps5Command"
}, $true)
$script:appxInstallScriptBlock = $installCommandAssignment.Right.Expression.ScriptBlock.GetScriptBlock()
function Write-WinUtilLog { function Write-WinUtilLog {
param($Message, $Level, $Component) param($Message, $Level, $Component)
@@ -16,25 +43,49 @@ BeforeAll {
function Invoke-WPFRunspace { function Invoke-WPFRunspace {
param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock) param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock)
} }
function Invoke-WPFUIThread {
param([scriptblock]$ScriptBlock)
}
function Set-WinUtilProgressBar {
param($Label, $Percent)
}
function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent)
}
function powershell.exe { }
function Get-AppxPackage { function Get-AppxPackage {
param($Name, [switch]$AllUsers) param($Name, [switch]$AllUsers, $ErrorAction)
}
function Add-AppxPackage {
param($Register, [switch]$DisableDevelopmentMode, $ErrorAction)
}
function Install-WinUtilWinget { }
function Install-WinUtilProgramWinget {
param($Action, $Programs)
} }
function Remove-AppxPackage { function Remove-AppxPackage {
param( param(
[Parameter(ValueFromPipeline = $true)] [Parameter(ValueFromPipeline = $true)]
$InputObject, $InputObject,
[switch]$AllUsers $Package,
[switch]$AllUsers,
$ErrorAction
) )
process { } process { }
} }
function Remove-WinUtilProvisionedAPPX {
param($PackageList)
}
function Get-AppxProvisionedPackage { function Get-AppxProvisionedPackage {
param([switch]$Online) param([switch]$Online, $ErrorAction)
} }
function Remove-AppxProvisionedPackage { function Remove-AppxProvisionedPackage {
param( param(
[Parameter(ValueFromPipeline = $true)] [Parameter(ValueFromPipeline = $true)]
$InputObject, $InputObject,
[switch]$Online [switch]$Online,
$PackageName,
$ErrorAction
) )
process { } process { }
} }
@@ -53,40 +104,361 @@ BeforeAll {
} }
} }
Describe "Get-WinUtilInstalledAPPX" {
BeforeEach {
Mock Write-WinUtilLog { }
Mock powershell.exe {
$global:LASTEXITCODE = 0
@("Example.One", "Example.Two")
}
}
It "queries installed package names through Windows PowerShell" {
$result = Get-WinUtilInstalledAPPX
$result | Should -Be @("Example.One", "Example.Two")
Should -Invoke -CommandName powershell.exe -Times 1 -Exactly
Should -Invoke -CommandName Write-WinUtilLog -Times 0 -Exactly
}
It "logs query failures and returns no package names" {
Mock powershell.exe {
$global:LASTEXITCODE = 1
"AppX query failed"
}
$result = @(Get-WinUtilInstalledAPPX)
$result | Should -HaveCount 0
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "AppX" -and
$Message -eq "Failed to get installed AppX packages: AppX query failed"
}
}
}
Describe "Install-WinUtilAPPX" {
BeforeEach {
Mock Write-WinUtilLog { }
Mock Install-WinUtilWinget { }
Mock Install-WinUtilProgramWinget { }
Mock powershell.exe {
$global:LASTEXITCODE = 0
"C:\Program Files\WindowsApps\Example.Package\AppxManifest.xml"
}
}
It "uses a local manifest without contacting the Microsoft Store" {
Install-WinUtilAPPX -Name "Example.Package" -StoreId "9EXAMPLE1234"
Should -Invoke -CommandName Install-WinUtilWinget -Times 0 -Exactly
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 0 -Exactly
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Component -eq "AppX" -and
$Message -like "Registered local AppX manifest for Example.Package*"
}
}
It "falls back to the Microsoft Store when no local manifest is available" {
Mock powershell.exe { $global:LASTEXITCODE = 0 }
Install-WinUtilAPPX -Name "Example.Package" -StoreId "9EXAMPLE1234"
Should -Invoke -CommandName Install-WinUtilWinget -Times 1 -Exactly
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
$Action -eq "Install" -and $Programs.Count -eq 1 -and $Programs[0] -eq "msstore:9EXAMPLE1234"
}
}
It "logs local registration failures before using the Microsoft Store" {
Mock powershell.exe {
$global:LASTEXITCODE = 1
"Registration failed"
}
Install-WinUtilAPPX -Name "Example.Package" -StoreId "9EXAMPLE1234"
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "WARN" -and
$Component -eq "AppX" -and
$Message -eq "Local AppX registration failed for Example.Package: Registration failed"
}
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly
}
It "throws after logging an error when neither install method is available" {
Mock powershell.exe { $global:LASTEXITCODE = 0 }
{ Install-WinUtilAPPX -Name "Example.Package" } |
Should -Throw "Unable to install Example.Package because no local manifest or Microsoft Store ID is available."
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 0 -Exactly
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "AppX" -and
$Message -eq "Unable to install Example.Package because no local manifest or Microsoft Store ID is available."
}
}
It "registers an installed package manifest through Windows PowerShell" {
Mock Get-AppxPackage {
[pscustomobject]@{
InstallLocation = "C:\Program Files\WindowsApps\Example.Package"
Version = [version]"2.0.0.0"
}
}
Mock Get-AppxProvisionedPackage { }
Mock Test-Path { $LiteralPath -eq "C:\Program Files\WindowsApps\Example.Package\AppxManifest.xml" }
Mock Add-AppxPackage { }
$result = & $script:appxInstallScriptBlock "Example.Package"
$result | Should -Be "C:\Program Files\WindowsApps\Example.Package\AppxManifest.xml"
Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly
Should -Invoke -CommandName Add-AppxPackage -Times 1 -Exactly
}
}
Describe "Get installed AppX selection" {
BeforeEach {
$script:sync = [Hashtable]::Synchronized(@{
ProcessRunning = $false
configs = @{
feature = @{}
appxHashtable = @{
WPFAppxExample = [pscustomobject]@{ PackageId = "Example.Package" }
WPFAppxMissing = [pscustomobject]@{ PackageId = "Missing.Package" }
}
}
WPFAppxExample = [pscustomobject]@{ IsChecked = $false }
WPFAppxMissing = [pscustomobject]@{ IsChecked = $false }
})
Mock Set-WinUtilProgressBar { }
Mock Set-WinUtilTweaksProgressIndicator { }
Mock Get-WinUtilInstalledAPPX { @("Example.Package") }
Mock Invoke-WPFAppxInstall { }
}
AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
}
It "selects configured packages returned by the compatibility-safe query" {
Invoke-WPFButton -Button "WPFGetInstalledAppx"
Should -Invoke -CommandName Get-WinUtilInstalledAPPX -Times 1 -Exactly
$script:sync.WPFAppxExample.IsChecked | Should -BeTrue
$script:sync.WPFAppxMissing.IsChecked | Should -BeFalse
}
It "routes the install button to the AppX install workflow" {
Invoke-WPFButton -Button "WPFInstallSelectedAppx"
Should -Invoke -CommandName Invoke-WPFAppxInstall -Times 1 -Exactly
}
}
Describe "Remove-WinUtilAPPX" { Describe "Remove-WinUtilAPPX" {
BeforeEach { BeforeEach {
Mock Write-Host { } Mock Write-Host { }
Mock Write-WinUtilLog { } Mock Write-WinUtilLog { }
Mock Get-AppxPackage { Mock Get-AppxPackage {
@(
[pscustomobject]@{ [pscustomobject]@{
Name = $Name Name = $Name
PackageFullName = "$Name.FullName"
} }
[pscustomobject]@{
Name = $Name
PackageFullName = "$Name.FullName"
}
)
} }
Mock Remove-AppxPackage { } Mock Remove-AppxPackage { }
}
It "removes matching installed AppX packages" {
Remove-WinUtilAPPX -Name "Microsoft.Xbox*"
Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly -ParameterFilter {
$Name -eq "*Microsoft.Xbox**" -and $AllUsers -eq $true
}
Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly -ParameterFilter {
$Package -eq "*Microsoft.Xbox**.FullName" -and
$AllUsers -eq $true -and
$ErrorAction -eq "Stop"
}
}
It "logs installed AppX removal failures" {
Mock Remove-AppxPackage { throw "Removal failed" }
{ Remove-WinUtilAPPX -Name "Example.Package" } | Should -Not -Throw
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
$Level -eq "ERROR" -and
$Component -eq "AppX" -and
$Message -eq "Failed to remove AppX package *Example.Package*.FullName: Removal failed"
}
}
}
Describe "Remove-WinUtilProvisionedAPPX" {
BeforeEach {
Mock Get-AppxProvisionedPackage { Mock Get-AppxProvisionedPackage {
@( @(
[pscustomobject]@{ DisplayName = "Microsoft.XboxGamingOverlay" } [pscustomobject]@{ DisplayName = "Example.One"; PackageName = "Example.One_1.0" }
[pscustomobject]@{ DisplayName = "Microsoft.WindowsCalculator" } [pscustomobject]@{ DisplayName = "Example.Two"; PackageName = "Example.Two_1.0" }
) )
} }
Mock Remove-AppxProvisionedPackage { } Mock Remove-AppxProvisionedPackage { }
} }
It "removes matching installed and provisioned AppX packages" { It "queries provisioned packages once for all selected package names" {
Remove-WinUtilAPPX -Name "Microsoft.Xbox*" & $script:provisionedRemovalScriptBlock "Example.One" "Example.Two"
Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly -ParameterFilter {
$Name -eq "Microsoft.Xbox*" -and $AllUsers -eq $true
}
Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly -ParameterFilter {
$InputObject.Name -eq "Microsoft.Xbox*" -and $AllUsers -eq $true
}
Should -Invoke -CommandName Get-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Get-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
$Online -eq $true $Online -eq $true
} }
Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
$InputObject.DisplayName -eq "Microsoft.XboxGamingOverlay" -and $Online -eq $true $PackageName -eq "Example.One_1.0" -and $Online -eq $true
} }
Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
$PackageName -eq "Example.Two_1.0" -and $Online -eq $true
}
}
It "surfaces provisioned package removal failures from the child process" {
Mock Remove-AppxProvisionedPackage { throw "DISM failed" }
{ & $script:provisionedRemovalScriptBlock "Example.One" } |
Should -Throw "*Failed to remove provisioned AppX package Example.One_1.0: DISM failed*"
Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
$PackageName -eq "Example.One_1.0" -and
$Online -eq $true -and
$ErrorAction -eq "Stop"
}
}
It "throws after logging child process failures" {
$source = Get-Content -Path $provisionedSourcePath -Raw
$source | Should -Match '\$removalOutput = powershell\.exe .* 2>&1'
$source | Should -Match 'Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message \$errorMessage'
$source | Should -Match '(?s)AppX provisioned package removal failed:.*throw \$errorMessage.*AppX provisioned package removal completed\.'
}
}
Describe "Invoke-WPFAppxInstall" {
BeforeEach {
$script:sync = [Hashtable]::Synchronized(@{
ProcessRunning = $false
Form = [pscustomobject]@{ Dispatcher = [pscustomobject]@{} }
selectedAppx = [System.Collections.Generic.List[string]]::new()
configs = @{
appxHashtable = @{
WPFAppxExample = [pscustomobject]@{
Content = "Example App"
PackageId = "Example.Package"
StoreId = "9EXAMPLE1234"
}
}
}
})
$script:capturedAppxInstallScriptBlock = $null
$script:capturedAppxInstallParameterList = $null
$script:appxInstallProcessRunningAtLaunch = $null
Mock Show-WinUtilMessage { "OK" }
Mock Write-Host { }
Mock Write-WinUtilLog { }
Mock Set-WinUtilTweaksProgressIndicator { }
Mock Invoke-WPFUIThread { }
Mock Install-WinUtilAPPX { }
Mock Invoke-WPFRunspace {
$script:appxInstallProcessRunningAtLaunch = $script:sync.ProcessRunning
$script:capturedAppxInstallScriptBlock = $ScriptBlock
$script:capturedAppxInstallParameterList = $ParameterList
[pscustomobject]@{ MockHandle = $true }
}
}
AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name capturedAppxInstallScriptBlock -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name capturedAppxInstallParameterList -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name appxInstallProcessRunningAtLaunch -Scope Script -ErrorAction SilentlyContinue
}
It "prompts and exits when no AppX packages are selected for install" {
Invoke-WPFAppxInstall
Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
$Message -eq "No AppX Package selected" -and
$Title -eq "Error" -and
$Button -eq "OK" -and
$Icon -eq "Error"
}
Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
}
It "prevents overlapping AppX install operations" {
$script:sync.ProcessRunning = $true
$script:sync.selectedAppx.Add("WPFAppxExample")
Invoke-WPFAppxInstall
Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
$Message -eq "An AppX process is currently running." -and
$Title -eq "WinUtil" -and
$Button -eq "OK" -and
$Icon -eq "Warning"
}
Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
}
It "installs selected AppX packages with their Store IDs" {
$script:sync.selectedAppx.Add("WPFAppxExample")
Invoke-WPFAppxInstall
$script:appxInstallProcessRunningAtLaunch | Should -BeTrue
& $script:capturedAppxInstallScriptBlock -selected @("WPFAppxExample") -apps $script:sync.configs.appxHashtable
$script:capturedAppxInstallParameterList[0][1][0] | Should -Be "WPFAppxExample"
Should -Invoke -CommandName Install-WinUtilAPPX -Times 1 -Exactly -ParameterFilter {
$Name -eq "Example.Package" -and $StoreId -eq "9EXAMPLE1234"
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Installing Example App (1/1)" -and $Percent -eq 0
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Installed Example App (1/1)" -and $Percent -eq 100
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "AppX install finished" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
}
$script:sync.ProcessRunning | Should -BeFalse
}
It "shows failure feedback and clears ProcessRunning when install fails" {
$script:sync.selectedAppx.Add("WPFAppxExample")
Mock Install-WinUtilAPPX { throw "Install failed" }
Invoke-WPFAppxInstall
& $script:capturedAppxInstallScriptBlock -selected @("WPFAppxExample") -apps $script:sync.configs.appxHashtable
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "AppX install failed" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
}
$script:sync.ProcessRunning | Should -BeFalse
} }
} }
@@ -101,9 +473,11 @@ Describe "Invoke-WPFAppxRemoval entrypoint" {
}) })
$script:capturedAppxScriptBlock = $null $script:capturedAppxScriptBlock = $null
$script:capturedAppxParameterList = $null $script:capturedAppxParameterList = $null
$script:appxRemovalProcessRunningAtLaunch = $null
Mock Show-WinUtilMessage { "OK" } Mock Show-WinUtilMessage { "OK" }
Mock Invoke-WPFRunspace { Mock Invoke-WPFRunspace {
$script:appxRemovalProcessRunningAtLaunch = $script:sync.ProcessRunning
$script:capturedAppxScriptBlock = $ScriptBlock $script:capturedAppxScriptBlock = $ScriptBlock
$script:capturedAppxParameterList = $ParameterList $script:capturedAppxParameterList = $ParameterList
[pscustomobject]@{ MockHandle = $true } [pscustomobject]@{ MockHandle = $true }
@@ -114,6 +488,7 @@ Describe "Invoke-WPFAppxRemoval entrypoint" {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name capturedAppxScriptBlock -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name capturedAppxScriptBlock -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name capturedAppxParameterList -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name capturedAppxParameterList -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name appxRemovalProcessRunningAtLaunch -Scope Script -ErrorAction SilentlyContinue
} }
It "prompts and exits when no AppX packages are selected" { It "prompts and exits when no AppX packages are selected" {
@@ -128,6 +503,21 @@ Describe "Invoke-WPFAppxRemoval entrypoint" {
Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
} }
It "prevents overlapping AppX removal operations" {
$script:sync.ProcessRunning = $true
$script:sync.selectedAppx.Add("WPFAppxExample")
Invoke-WPFAppxRemoval
Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
$Message -eq "An AppX process is currently running." -and
$Title -eq "WinUtil" -and
$Button -eq "OK" -and
$Icon -eq "Warning"
}
Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
}
It "passes selected AppX keys and app metadata to the removal runspace" { It "passes selected AppX keys and app metadata to the removal runspace" {
$script:sync.selectedAppx.Add("WPFAppxExample") $script:sync.selectedAppx.Add("WPFAppxExample")
$script:sync.configs.appxHashtable["WPFAppxExample"] = [pscustomobject]@{ $script:sync.configs.appxHashtable["WPFAppxExample"] = [pscustomobject]@{
@@ -136,7 +526,10 @@ Describe "Invoke-WPFAppxRemoval entrypoint" {
} }
Invoke-WPFAppxRemoval Invoke-WPFAppxRemoval
$script:sync.selectedAppx.Add("WPFAppxChangedAfterLaunch")
$script:appxRemovalProcessRunningAtLaunch | Should -BeTrue
$script:capturedAppxParameterList[0][1] | Should -HaveCount 1
Should -Invoke -CommandName Show-WinUtilMessage -Times 0 -Exactly Should -Invoke -CommandName Show-WinUtilMessage -Times 0 -Exactly
Should -Invoke -CommandName Invoke-WPFRunspace -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Invoke-WPFRunspace -Times 1 -Exactly -ParameterFilter {
$ScriptBlock -is [scriptblock] -and $ScriptBlock -is [scriptblock] -and
@@ -153,6 +546,7 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
BeforeEach { BeforeEach {
$script:sync = [Hashtable]::Synchronized(@{ $script:sync = [Hashtable]::Synchronized(@{
ProcessRunning = $false ProcessRunning = $false
Form = [pscustomobject]@{ Dispatcher = [pscustomobject]@{} }
selectedAppx = [System.Collections.Generic.List[string]]::new() selectedAppx = [System.Collections.Generic.List[string]]::new()
configs = @{ configs = @{
appxHashtable = @{} appxHashtable = @{}
@@ -185,14 +579,18 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
Mock Show-WinUtilMessage { "OK" } Mock Show-WinUtilMessage { "OK" }
Mock Write-Host { } Mock Write-Host { }
Mock Write-WinUtilLog { } Mock Write-WinUtilLog { }
Mock Set-WinUtilTweaksProgressIndicator { }
Mock Invoke-WPFUIThread { }
Mock Stop-Process { } Mock Stop-Process { }
Mock Set-ItemProperty { } Mock Set-ItemProperty { }
Mock Get-AppxPackage { Mock Get-AppxPackage {
[pscustomobject]@{ [pscustomobject]@{
Name = $Name Name = $Name
PackageFullName = "$Name.FullName"
} }
} }
Mock Remove-AppxPackage { } Mock Remove-AppxPackage { }
Mock Remove-WinUtilProvisionedAPPX { }
Mock Get-Package { Mock Get-Package {
[pscustomobject]@{ [pscustomobject]@{
Name = "Microsoft Teams Meeting Add-in" Name = "Microsoft Teams Meeting Add-in"
@@ -216,10 +614,84 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
& $script:capturedAppxScriptBlock -selected $selected -apps $script:apps & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps
Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly -ParameterFilter {
$Name -eq "Example.Package" -and $AllUsers -eq $true $Name -eq "*Example.Package*" -and $AllUsers -eq $true
} }
Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly -ParameterFilter {
$InputObject.Name -eq "Example.Package" -and $AllUsers -eq $true $Package -eq "*Example.Package*.FullName"
}
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
$PackageList.Count -eq 1 -and $PackageList[0] -eq "Example.Package"
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Removing Example App (1/1)" -and $Percent -eq 0
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "Removed Example App (1/1)" -and $Percent -eq 90
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "AppX removal finished" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
}
$script:sync.ProcessRunning | Should -BeFalse
}
It "shows failure feedback and clears ProcessRunning when removal fails" {
$selected = @("WPFAppxExample")
$script:sync.selectedAppx.Add("WPFAppxExample")
$script:sync.configs.appxHashtable = $script:apps
Mock Remove-WinUtilAPPX { throw "Removal failed" }
Invoke-WPFAppxRemoval
& $script:capturedAppxScriptBlock -selected $selected -apps $script:apps
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "AppX removal failed" -and $Percent -eq 100
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
}
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 0 -Exactly
$script:sync.ProcessRunning | Should -BeFalse
}
It "removes packages without UI progress during headless autorun" {
$selected = @("WPFAppxExample")
$script:sync.Remove("Form")
$script:sync.selectedAppx.Add("WPFAppxExample")
$script:sync.configs.appxHashtable = $script:apps
Invoke-WPFAppxRemoval
& $script:capturedAppxScriptBlock -selected $selected -apps $script:apps
Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 0 -Exactly
Should -Invoke -CommandName Invoke-WPFUIThread -Times 0 -Exactly
$script:sync.ProcessRunning | Should -BeFalse
}
It "shows failure feedback when provisioned package removal fails" {
$selected = @("WPFAppxExample")
$script:sync.selectedAppx.Add("WPFAppxExample")
$script:sync.configs.appxHashtable = $script:apps
Mock Remove-WinUtilProvisionedAPPX { throw "Provisioned removal failed" }
Invoke-WPFAppxRemoval
& $script:capturedAppxScriptBlock -selected $selected -apps $script:apps
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $true -and $Label -eq "AppX removal failed" -and $Percent -eq 100
}
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 0 -Exactly -ParameterFilter {
$Label -eq "AppX removal finished"
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
}
Should -Invoke -CommandName Invoke-WPFUIThread -Times 0 -Exactly -ParameterFilter {
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
} }
$script:sync.ProcessRunning | Should -BeFalse $script:sync.ProcessRunning | Should -BeFalse
} }
@@ -239,7 +711,10 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
& $script:capturedAppxScriptBlock -selected $selected -apps $script:apps & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps
Should -Invoke -CommandName Stop-Process -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Stop-Process -Times 1 -Exactly -ParameterFilter {
$Name -eq "GameBarFTServer" $Name -eq "GameBarFTServer" -and
$Force -eq $true -and
$Confirm -eq $false -and
$ErrorAction -eq "SilentlyContinue"
} }
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
$Path -eq "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR" -and $Path -eq "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR" -and
@@ -247,7 +722,10 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
$Value -eq 0 $Value -eq 0
} }
Should -Invoke -CommandName Stop-Process -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Stop-Process -Times 1 -Exactly -ParameterFilter {
$Name -eq "dllhost" $Name -eq "dllhost" -and
$Force -eq $true -and
$Confirm -eq $false -and
$ErrorAction -eq "SilentlyContinue"
} }
Should -Invoke -CommandName Get-Package -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Get-Package -Times 1 -Exactly -ParameterFilter {
$Name -eq "Microsoft Teams*" $Name -eq "Microsoft Teams*"
@@ -256,6 +734,12 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
$InputObject.Name -eq "Microsoft Teams Meeting Add-in" -and $Force -eq $true $InputObject.Name -eq "Microsoft Teams Meeting Add-in" -and $Force -eq $true
} }
Should -Invoke -CommandName Remove-AppxPackage -Times 3 -Exactly Should -Invoke -CommandName Remove-AppxPackage -Times 3 -Exactly
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
$PackageList.Count -eq 3 -and
$PackageList[0] -eq "Microsoft.XboxGamingOverlay" -and
$PackageList[1] -eq "Microsoft.WindowsNotepad" -and
$PackageList[2] -eq "MSTeams"
}
$script:sync.ProcessRunning | Should -BeFalse $script:sync.ProcessRunning | Should -BeFalse
} }
} }
+5
View File
@@ -326,6 +326,11 @@ Describe "UI-rendered config entries" {
foreach ($missingField in (Get-WinUtilMissingRequiredFields -EntryName $entry.Name -Entry $entry.Value -RequiredFields $requiredFields)) { foreach ($missingField in (Get-WinUtilMissingRequiredFields -EntryName $entry.Name -Entry $entry.Value -RequiredFields $requiredFields)) {
$invalidEntries.Add($missingField) $invalidEntries.Add($missingField)
} }
if ((Test-WinUtilHasProperty -Object $entry.Value -Name "StoreId") -and
$entry.Value.StoreId -notmatch '^(?:[A-Z0-9]{12}|XP[A-Z0-9]{12})$') {
$invalidEntries.Add("$($entry.Name) has invalid Microsoft Store ID '$($entry.Value.StoreId)'")
}
} }
if ($invalidEntries.Count -gt 0) { if ($invalidEntries.Count -gt 0) {
+2 -1
View File
@@ -21,6 +21,7 @@ Describe "Install app rendering startup contract" {
$renderScript | Should -Match 'Dispatcher\.BeginInvoke' $renderScript | Should -Match 'Dispatcher\.BeginInvoke'
$renderScript | Should -Match 'Invoke-WinUtilInstallAppRenderNextBatch' $renderScript | Should -Match 'Invoke-WinUtilInstallAppRenderNextBatch'
$renderScript | Should -Match 'Initialize-InstallAppEntry' $renderScript | Should -Match 'Initialize-InstallAppEntry'
$renderScript | Should -Match 'Find-AppsByNameOrDescription -SearchString \$sync\.SearchBar\.Text -Category \$sync\.SearchBar\.Tag'
$renderScript | Should -Match '\$sync\.InstallAppEntriesRendered = \$true' $renderScript | Should -Match '\$sync\.InstallAppEntriesRendered = \$true'
} }
@@ -59,7 +60,7 @@ Describe "Install app rendering startup contract" {
} }
function global:Find-AppsByNameOrDescription { function global:Find-AppsByNameOrDescription {
param($SearchString) param($SearchString, $Category)
throw "Search should not run for an empty search box in this test." throw "Search should not run for an empty search box in this test."
} }
+22
View File
@@ -172,18 +172,27 @@ namespace Windows.Controls
WPFInstallBrowser = [pscustomobject]@{ WPFInstallBrowser = [pscustomobject]@{
Content = "Firefox" Content = "Firefox"
Description = "Fast private browser" Description = "Fast private browser"
Category = "Browsers"
} }
WPFInstallMedia = [pscustomobject]@{ WPFInstallMedia = [pscustomobject]@{
Content = "VLC" Content = "VLC"
Description = "Media player" Description = "Media player"
Category = "Multimedia Tools"
} }
WPFInstallLiteral = [pscustomobject]@{ WPFInstallLiteral = [pscustomobject]@{
Content = "Tool [abc]" Content = "Tool [abc]"
Description = "Literal wildcard sample" Description = "Literal wildcard sample"
Category = "Utilities"
} }
WPFInstallEditor = [pscustomobject]@{ WPFInstallEditor = [pscustomobject]@{
Content = "Code Editor" Content = "Code Editor"
Description = "Text editing" Description = "Text editing"
Category = "Development"
}
WPFInstallPowerToys = [pscustomobject]@{
Content = "PowerToys"
Description = "A collection of system utilities"
Category = "Microsoft Tools"
} }
} }
} }
@@ -363,6 +372,19 @@ Describe "Find-AppsByNameOrDescription" {
$mediaItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed) $mediaItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
$category.Visibility | Should -Be ([Windows.Visibility]::Visible) $category.Visibility | Should -Be ([Windows.Visibility]::Visible)
} }
It "filters category chips by exact application category" {
$utilityItem = New-WinUtilAppSearchItem -Tag "WPFInstallLiteral"
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($utilityItem, $powerToysItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -SearchString "Utilities" -Category "Utilities"
$utilityItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
$powerToysItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
$category.Visibility | Should -Be ([Windows.Visibility]::Visible)
}
} }
Describe "Find-TweaksByNameOrDescription" { Describe "Find-TweaksByNameOrDescription" {
+11
View File
@@ -19,6 +19,9 @@ BeforeAll {
function Remove-WinUtilAPPX { function Remove-WinUtilAPPX {
param($Name) param($Name)
} }
function Remove-WinUtilProvisionedAPPX {
param($PackageList)
}
function Set-WinUtilDNS { function Set-WinUtilDNS {
param($DNSProvider) param($DNSProvider)
} }
@@ -31,6 +34,9 @@ BeforeAll {
function Set-WinUtilProgressBar { function Set-WinUtilProgressBar {
param($Label, $Percent) param($Label, $Percent)
} }
function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent)
}
function Write-WinUtilLog { function Write-WinUtilLog {
param($Message, $Level, $Component) param($Message, $Level, $Component)
} }
@@ -89,6 +95,7 @@ Describe "Invoke-WinUtilTweaks" {
Mock Set-WinUtilRegistry { } Mock Set-WinUtilRegistry { }
Mock Invoke-WinUtilScript { } Mock Invoke-WinUtilScript { }
Mock Remove-WinUtilAPPX { } Mock Remove-WinUtilAPPX { }
Mock Remove-WinUtilProvisionedAPPX { }
Mock Write-WinUtilLog { } Mock Write-WinUtilLog { }
Mock Write-Warning { } Mock Write-Warning { }
} }
@@ -118,6 +125,9 @@ Describe "Invoke-WinUtilTweaks" {
Should -Invoke -CommandName Remove-WinUtilAPPX -Times 1 -Exactly -ParameterFilter { Should -Invoke -CommandName Remove-WinUtilAPPX -Times 1 -Exactly -ParameterFilter {
$Name -eq "Microsoft.ExampleApp" $Name -eq "Microsoft.ExampleApp"
} }
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
$PackageList.Count -eq 1 -and $PackageList[0] -eq "Microsoft.ExampleApp"
}
} }
It "uses original registry values and service startup types in undo mode" { It "uses original registry values and service startup types in undo mode" {
@@ -137,6 +147,7 @@ Describe "Invoke-WinUtilTweaks" {
$Name -eq "WPFTweaksExample" -and $ScriptBlock.ToString() -eq "Write-Output 'undo tweak'" $Name -eq "WPFTweaksExample" -and $ScriptBlock.ToString() -eq "Write-Output 'undo tweak'"
} }
Should -Invoke -CommandName Remove-WinUtilAPPX -Times 0 -Exactly Should -Invoke -CommandName Remove-WinUtilAPPX -Times 0 -Exactly
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 0 -Exactly
} }
It "keeps a user-changed service startup type by default" { It "keeps a user-changed service startup type by default" {
+92 -1
View File
@@ -5,7 +5,7 @@
BeforeAll { BeforeAll {
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
if (-not ("System.Windows.Controls.CheckBox" -as [type])) { if (-not ("Windows.Visibility" -as [type])) {
Add-Type @" Add-Type @"
namespace Windows namespace Windows
{ {
@@ -15,7 +15,25 @@ namespace Windows
Collapsed Collapsed
} }
} }
"@
}
if (-not ("Windows.WindowState" -as [type])) {
Add-Type @"
namespace Windows
{
public enum WindowState
{
Normal,
Minimized,
Maximized
}
}
"@
}
if (-not ("System.Windows.Controls.CheckBox" -as [type])) {
Add-Type @"
namespace System.Windows.Controls namespace System.Windows.Controls
{ {
public class CheckBox public class CheckBox
@@ -44,8 +62,17 @@ namespace System.Windows.Controls
. (Join-Path $script:repoRoot "functions\private\Update-WinUtilSelections.ps1") . (Join-Path $script:repoRoot "functions\private\Update-WinUtilSelections.ps1")
. (Join-Path $script:repoRoot "functions\private\Reset-WPFCheckBoxes.ps1") . (Join-Path $script:repoRoot "functions\private\Reset-WPFCheckBoxes.ps1")
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFSelectedCheckboxesUpdate.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") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFToggleAllCategories.ps1")
function Set-WinUtilProgressBar {
param($Label, $Percent)
}
function Set-WinUtilTweaksProgressIndicator {
param($Visible, $Label, $Percent)
}
function script:New-WinUtilFakeCheckBox { function script:New-WinUtilFakeCheckBox {
param([bool]$IsChecked = $false) param([bool]$IsChecked = $false)
@@ -272,3 +299,67 @@ Describe "Invoke-WPFToggleAllCategories" {
} }
} }
} }
Describe "Invoke-WPFButton progress cleanup" {
BeforeEach {
New-WinUtilUiStateTestContext
Mock Set-WinUtilProgressBar { }
Mock Set-WinUtilTweaksProgressIndicator { }
}
AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
}
It "clears completed progress on the next idle button click" {
$script:sync.ProcessRunning = $false
Invoke-WPFButton -Button "WPFNoOp"
Should -Invoke Set-WinUtilProgressBar -Times 1 -Exactly -ParameterFilter {
$Label -eq "" -and $Percent -eq 0
}
Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
$Visible -eq $false
}
}
It "leaves progress visible while a process is running" {
$script:sync.ProcessRunning = $true
Invoke-WPFButton -Button "WPFNoOp"
Should -Not -Invoke Set-WinUtilProgressBar
Should -Not -Invoke Set-WinUtilTweaksProgressIndicator
}
}
Describe "Invoke-WPFButton window state" {
BeforeEach {
New-WinUtilUiStateTestContext
$script:sync.ProcessRunning = $true
$script:sync.Form = [pscustomobject]@{
WindowState = [Windows.WindowState]::Normal
}
}
AfterEach {
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
}
It "maximizes a normal window" {
Invoke-WPFButton -Button "WPFMaximizeButton"
$script:sync.Form.WindowState | Should -Be ([Windows.WindowState]::Maximized)
}
It "restores a maximized window" {
$script:sync.Form.WindowState = [Windows.WindowState]::Maximized
Invoke-WPFButton -Button "WPFMaximizeButton"
$script:sync.Form.WindowState | Should -Be ([Windows.WindowState]::Normal)
}
}
+132 -1
View File
@@ -134,9 +134,20 @@ Describe "XAML document" {
"WPFTab3BT", "WPFTab3BT",
"WPFTab4BT", "WPFTab4BT",
"WPFTab5BT", "WPFTab5BT",
"WPFTab6BT",
"SearchBar", "SearchBar",
"SearchBarIcon",
"SearchBarClearButton", "SearchBarClearButton",
"WPFSearchChips",
"WPFSearchChipAll",
"WPFSearchChipBrowsers",
"WPFSearchChipCommunications",
"WPFSearchChipDevelopment",
"WPFSearchChipGames",
"WPFSearchChipMicrosoftTools",
"WPFSearchChipMultimediaTools",
"WPFSearchChipProTools",
"WPFSearchChipSelfhostedTools",
"WPFSearchChipUtilities",
"appscategory", "appscategory",
"appspanel", "appspanel",
"tweakspanel", "tweakspanel",
@@ -148,6 +159,7 @@ Describe "XAML document" {
"WPFAdvanced", "WPFAdvanced",
"WPFClearTweaksSelection", "WPFClearTweaksSelection",
"WPFGetInstalledTweaks", "WPFGetInstalledTweaks",
"WPFAppxRemoval",
"WPFTweaksbutton", "WPFTweaksbutton",
"WPFUndoall", "WPFUndoall",
"WPFUpdatesdefault", "WPFUpdatesdefault",
@@ -157,6 +169,8 @@ Describe "XAML document" {
"WPFGetInstalledAppx", "WPFGetInstalledAppx",
"WPFSelectAllAppx", "WPFSelectAllAppx",
"WPFClearAppxSelection", "WPFClearAppxSelection",
"WPFBackToTweaks",
"WPFInstallSelectedAppx",
"WPFRemoveSelectedAppx" "WPFRemoveSelectedAppx"
) )
@@ -223,6 +237,122 @@ Describe "XAML document" {
} }
} }
} }
It "opens AppX removal from Tweaks and provides a return path" {
$navPanel = $script:xaml.SelectSingleNode('//*[local-name()="StackPanel"][@Name="NavDockPanel"]')
$tweaksTab = $script:xaml.SelectSingleNode('//*[local-name()="TabItem"][@Name="WPFTab2"]')
$appxTab = $script:xaml.SelectSingleNode('//*[local-name()="TabItem"][@Name="WPFTab6"]')
$openButton = $tweaksTab.SelectSingleNode('.//*[local-name()="Button"][@Name="WPFAppxRemoval"]')
$buttonNames = @($openButton.ParentNode.SelectNodes('./*[local-name()="Button"]') | ForEach-Object { $_.GetAttribute("Name") })
$getInstalledIndex = [array]::IndexOf($buttonNames, "WPFGetInstalledTweaks")
$openAppxIndex = [array]::IndexOf($buttonNames, "WPFAppxRemoval")
$buttonSource = Get-Content -Path $script:buttonScriptPath -Raw
$tabSource = Get-Content -Path (Join-Path $script:functionRoot "public\Invoke-WPFTab.ps1") -Raw
$navPanel.SelectSingleNode('./*[local-name()="ToggleButton"][@Name="WPFTab6BT"]') | Should -BeNullOrEmpty
$openButton.GetAttribute("Content").Trim() | Should -Be "AppX Removal"
$openAppxIndex | Should -Be ($getInstalledIndex + 1)
$appxTab.SelectSingleNode('.//*[local-name()="Button"][@Name="WPFBackToTweaks"]') | Should -Not -BeNullOrEmpty
$appxTab.SelectSingleNode('.//*[local-name()="Button"][@Name="WPFInstallSelectedAppx"]') | Should -Not -BeNullOrEmpty
$appxTab.SelectSingleNode('.//*[local-name()="Button"][@Name="WPFRemoveSelectedAppx"]') | Should -Not -BeNullOrEmpty
$buttonSource | Should -Match '"WPFAppxRemoval"\s*\{Invoke-WPFTab "WPFTab6BT"\}'
$buttonSource | Should -Match '"WPFBackToTweaks"\s*\{Invoke-WPFTab "WPFTab2BT"\}'
$buttonSource | Should -Match '"WPFInstallSelectedAppx"\s*\{Invoke-WPFAppxInstall\}'
$tabSource | Should -Match '\$sync\.\$tabNav\.Items\[\$tabNumber\]\.IsSelected = \$true'
}
It "centers top bar controls vertically" {
$navPanel = $script:xaml.SelectSingleNode('//*[local-name()="StackPanel"][@Name="NavDockPanel"]')
$minimizeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFMinimizeButton"]')
$actionPanel = $minimizeButton.ParentNode
$topBarButtonNames = @(
"ThemeButton",
"FontScalingButton",
"SettingsButton",
"WPFMinimizeButton",
"WPFMaximizeButton",
"WPFCloseButton"
)
$navPanel.GetAttribute("VerticalAlignment") | Should -Be "Center"
$actionPanel.GetAttribute("VerticalAlignment") | Should -Be "Center"
foreach ($buttonName in $topBarButtonNames) {
$button = $script:xaml.SelectSingleNode("//*[local-name()='Button'][@Name='$buttonName']")
$button.GetAttribute("VerticalAlignment") | Should -Be "Center"
}
}
It "keeps the responsive search controls within the available screen width" {
$window = $script:xaml.DocumentElement
$searchBar = $script:xaml.SelectSingleNode('//*[local-name()="TextBox"][@Name="SearchBar"]')
$searchBorder = $searchBar.ParentNode.ParentNode
$mainScript = Get-Content -Path $script:mainScriptPath -Raw
$window.GetAttribute("MinWidth") | Should -Be "800"
$searchBorder.GetAttribute("Width") | Should -BeNullOrEmpty
$searchBorder.GetAttribute("HorizontalAlignment") | Should -Be "Stretch"
$searchBar.GetAttribute("Width") | Should -BeNullOrEmpty
$searchBar.GetAttribute("HorizontalAlignment") | Should -Be "Stretch"
$mainScript | Should -Match '\$sync\.Form\.MinWidth = "1150"'
$mainScript | Should -Match '\$sync\.Form\.MinWidth = \[Math\]::Min\(\[double\]\$sync\.Form\.MinWidth, \[double\]\$screenWidth\)'
}
It "shows only one search action glyph at a time" {
$searchIcon = $script:xaml.SelectSingleNode('//*[local-name()="TextBlock"][@Name="SearchBarIcon"]')
$clearButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="SearchBarClearButton"]')
$mainScript = Get-Content -Path $script:mainScriptPath -Raw
$searchIcon | Should -Not -BeNullOrEmpty
$clearButton | Should -Not -BeNullOrEmpty
$mainScript | Should -Match '\$sync\.SearchBarClearButton\.Visibility = "Visible"\s+\$sync\.SearchBarIcon\.Visibility = "Collapsed"'
$mainScript | Should -Match '\$sync\.SearchBarClearButton\.Visibility = "Collapsed"\s+\$sync\.SearchBarIcon\.Visibility = "Visible"'
}
It "scopes toggle button styles without leaking into combo boxes" {
$resources = $script:xaml.SelectSingleNode('//*[local-name()="Window.Resources"]')
$implicitToggleStyles = @($resources.SelectNodes('./*[local-name()="Style"][@TargetType="ToggleButton" or @TargetType="{x:Type ToggleButton}"][not(@x:Key)]', $script:xamlNamespace))
$tabStyle = $resources.SelectSingleNode('./*[local-name()="Style"][@x:Key="TabToggleButton"]', $script:xamlNamespace)
$comboToggleStyle = $resources.SelectSingleNode('./*[local-name()="Style"][@x:Key="ComboBoxToggleButtonStyle"]', $script:xamlNamespace)
$comboStyle = $resources.SelectSingleNode('./*[local-name()="Style"][@TargetType="ComboBox"]')
$comboToggle = $comboStyle.SelectSingleNode('.//*[local-name()="ToggleButton"][@Name="ToggleButton"]')
$comboItemStyle = $resources.SelectSingleNode('./*[local-name()="Style"][@TargetType="ComboBoxItem"]')
$navButtons = @($script:xaml.SelectNodes('//*[local-name()="StackPanel"][@Name="NavDockPanel"]/*[local-name()="ToggleButton"]'))
$implicitToggleStyles | Should -BeNullOrEmpty
$tabStyle | Should -Not -BeNullOrEmpty
$comboToggleStyle | Should -Not -BeNullOrEmpty
$comboToggle.GetAttribute("Style") | Should -Be "{StaticResource ComboBoxToggleButtonStyle}"
$comboItemStyle | Should -Not -BeNullOrEmpty
$navButtons.Count | Should -Be 5
foreach ($navButton in $navButtons) {
$navButton.GetAttribute("Style") | Should -Be "{StaticResource TabToggleButton}"
}
}
It "uses state-aware maximize and restore icons" {
$themes = Get-WinUtilConfigObject -Name "themes"
$minimizeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFMinimizeButton"]')
$maximizeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFMaximizeButton"]')
$closeButton = $script:xaml.SelectSingleNode('//*[local-name()="Button"][@Name="WPFCloseButton"]')
$maximizeStyle = $maximizeButton.SelectSingleNode('./*[local-name()="Button.Style"]/*[local-name()="Style"]')
$maximizeIcon = $maximizeStyle.SelectSingleNode('./*[local-name()="Setter"][@Property="Content"]')
$maximizedTrigger = $maximizeStyle.SelectSingleNode('./*[local-name()="Style.Triggers"]/*[local-name()="DataTrigger"][@Value="Maximized"]')
$restoreIcon = $maximizedTrigger.SelectSingleNode('./*[local-name()="Setter"][@Property="Content"]')
foreach ($button in @($minimizeButton, $maximizeButton, $closeButton)) {
$button.GetAttribute("FontFamily") | Should -Be "Segoe MDL2 Assets"
$button.GetAttribute("FontSize") | Should -Be "{DynamicResource CloseIconFontSize}"
$button.GetAttribute("Margin") | Should -BeIn @("0", "0,0,0,0")
}
$minimizeButton.GetAttribute("Content") | Should -Be ([string][char]0xE921)
$maximizeIcon.GetAttribute("Value") | Should -Be ([string][char]0xE922)
$restoreIcon.GetAttribute("Value") | Should -Be ([string][char]0xE923)
$closeButton.GetAttribute("Content") | Should -Be ([string][char]0xE8BB)
$maximizeStyle.GetAttribute("BasedOn") | Should -Be "{StaticResource HoverButtonStyle}"
[int]$themes.shared.CloseIconFontSize | Should -BeLessThan ([int]$themes.shared.SettingsIconFontSize)
}
} }
Describe "XAML and sync wiring" { Describe "XAML and sync wiring" {
@@ -310,6 +440,7 @@ Describe "XAML and sync wiring" {
"InstallAppEntriesRendered", "InstallAppEntriesRendered",
"ProgressBar", "ProgressBar",
"progressBarTextBlock", "progressBarTextBlock",
"FontScaleFactor",
"Win11ISOImageInfo", "Win11ISOImageInfo",
"Win11ISODriveLetter", "Win11ISODriveLetter",
"Win11ISOWimPath", "Win11ISOWimPath",
+41 -2
View File
@@ -210,6 +210,8 @@ $sync.SearchBarClearButton.Add_Click({
}) })
# add some shortcuts for people that don't like clicking # add some shortcuts for people that don't like clicking
function Invoke-WinUtilFontScaleStep([double]$Step) { $sync.FontScalingSlider.Value = [math]::Max(0.75, [math]::Min(2.0, $sync.FontScalingSlider.Value + $Step)); Invoke-WinUtilFontScaling -ScaleFactor $sync.FontScalingSlider.Value }
$commonKeyEvents = { $commonKeyEvents = {
# Prevent shortcuts from executing if a process is already running # Prevent shortcuts from executing if a process is already running
if ($sync.ProcessRunning -eq $true) { if ($sync.ProcessRunning -eq $true) {
@@ -233,13 +235,25 @@ $commonKeyEvents = {
} }
# Handle Ctrl key combinations for specific actions # Handle Ctrl key combinations for specific actions
if ($_.KeyboardDevice.Modifiers -eq "Ctrl") { if ($_.KeyboardDevice.Modifiers -eq "Ctrl") {
$keyEventArgs = $_
switch ($_.Key) { switch ($_.Key) {
"F" { $sync.SearchBar.Focus() } # Focus on the search bar "F" { $sync.SearchBar.Focus() } # Focus on the search bar
"Q" { $this.Close() } # Close the application "Q" { $this.Close() } # Close the application
} }
} }
$ctrlShiftModifiers = [Windows.Input.ModifierKeys]::Control -bor [Windows.Input.ModifierKeys]::Shift
if ($_.KeyboardDevice.Modifiers -eq "Ctrl" -or $_.KeyboardDevice.Modifiers -eq $ctrlShiftModifiers) {
$keyEventArgs = $_
switch ($_.Key) {
{ $_ -in "OemPlus", "Add" } { Invoke-WinUtilFontScaleStep 0.05; $keyEventArgs.Handled = $true }
{ $_ -in "OemMinus", "Subtract" } { Invoke-WinUtilFontScaleStep -0.05; $keyEventArgs.Handled = $true }
}
}
} }
$sync["Form"].Add_PreViewKeyDown($commonKeyEvents) $sync["Form"].Add_PreViewKeyDown($commonKeyEvents)
$sync["Form"].Add_PreviewMouseWheel({
if ([Windows.Input.Keyboard]::Modifiers -eq "Ctrl") { Invoke-WinUtilFontScaleStep $(if ($_.Delta -gt 0) { 0.05 } else { -0.05 }); $_.Handled = $true }
})
$sync["Form"].Add_MouseLeftButtonDown({ $sync["Form"].Add_MouseLeftButtonDown({
Invoke-WPFPopup -Action "Hide" -Popups @("Settings", "Theme", "FontScaling") Invoke-WPFPopup -Action "Hide" -Popups @("Settings", "Theme", "FontScaling")
@@ -271,6 +285,7 @@ $sync["Form"].Add_ContentRendered({
# Extract screen width and height for the primary monitor # Extract screen width and height for the primary monitor
$screenWidth = $primaryScreen.Bounds.Width $screenWidth = $primaryScreen.Bounds.Width
$screenHeight = $primaryScreen.Bounds.Height $screenHeight = $primaryScreen.Bounds.Height
$sync.Form.MinWidth = [Math]::Min([double]$sync.Form.MinWidth, [double]$screenWidth)
# Compare with the primary monitor size # Compare with the primary monitor size
if ($sync.Form.ActualWidth -gt $screenWidth -or $sync.Form.ActualHeight -gt $screenHeight) { if ($sync.Form.ActualWidth -gt $screenWidth -or $sync.Form.ActualHeight -gt $screenHeight) {
@@ -326,7 +341,7 @@ $searchBarTimer.add_Tick({
$searchBarTimer.Stop() $searchBarTimer.Stop()
switch ($sync.currentTab) { switch ($sync.currentTab) {
"Install" { "Install" {
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Category $sync.SearchBar.Tag
} }
"Tweaks" { "Tweaks" {
Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text
@@ -337,21 +352,45 @@ $searchBarTimer.add_Tick({
} }
}) })
$sync["SearchBar"].Add_TextChanged({ $sync["SearchBar"].Add_TextChanged({
if ($sync.SearchBar.Tag -ne $sync.SearchBar.Text) {
$sync.SearchBar.Tag = $null
}
if ($sync.SearchBar.Text -ne "") { if ($sync.SearchBar.Text -ne "") {
$sync.SearchBarClearButton.Visibility = "Visible" $sync.SearchBarClearButton.Visibility = "Visible"
$sync.SearchBarIcon.Visibility = "Collapsed"
} else { } else {
$sync.SearchBarClearButton.Visibility = "Collapsed" $sync.SearchBarClearButton.Visibility = "Collapsed"
$sync.SearchBarIcon.Visibility = "Visible"
} }
# Category chip handlers apply their filter immediately.
if ($sync.SearchBar.Tag -eq $sync.SearchBar.Text) {
return
}
if ($searchBarTimer.IsEnabled) { if ($searchBarTimer.IsEnabled) {
$searchBarTimer.Stop() $searchBarTimer.Stop()
} }
$searchBarTimer.Start() $searchBarTimer.Start()
}) })
# Quick Category Search Chips
$sync["WPFSearchChipAll"].Add_Click({ Set-WinUtilAppCategoryFilter })
$sync["WPFSearchChipBrowsers"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Browsers" })
$sync["WPFSearchChipCommunications"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Communications" })
$sync["WPFSearchChipDevelopment"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Development" })
$sync["WPFSearchChipGames"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Games" })
$sync["WPFSearchChipMicrosoftTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Microsoft Tools" })
$sync["WPFSearchChipMultimediaTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Multimedia Tools" })
$sync["WPFSearchChipProTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Pro Tools" })
$sync["WPFSearchChipSelfhostedTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Selfhosted Tools" })
$sync["WPFSearchChipUtilities"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Utilities" })
$sync["Form"].Add_Loaded({ $sync["Form"].Add_Loaded({
param($e) param($e)
$null = $e $null = $e
$sync.Form.MinWidth = "1000" $sync.Form.MinWidth = "1150"
$sync["Form"].MaxWidth = [Double]::PositiveInfinity $sync["Form"].MaxWidth = [Double]::PositiveInfinity
$sync["Form"].MaxHeight = [Double]::PositiveInfinity $sync["Form"].MaxHeight = [Double]::PositiveInfinity
}) })
+4 -4
View File
@@ -433,10 +433,10 @@ $scripts = @(
reg.exe delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" /v DODownloadMode /f; reg.exe delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" /v DODownloadMode /f;
reg.exe add "HKLM\Software\Policies\Microsoft\Windows\OneDrive" /v DisableFileSyncNGSC /t REG_DWORD /d 0 /f; reg.exe add "HKLM\Software\Policies\Microsoft\Windows\OneDrive" /v DisableFileSyncNGSC /t REG_DWORD /d 0 /f;
reg.exe add "HKCU\Software\Microsoft\Windows\CurrentVersion\GameDVR" /v AppCaptureEnabled /t REG_DWORD /d 0 /f; reg.exe add "HKCU\Software\Microsoft\Windows\CurrentVersion\GameDVR" /v AppCaptureEnabled /t REG_DWORD /d 0 /f;
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\BITS" /v Start /t REG_DWORD /d 3 /f; $services = @{ BITS = 'Manual'; wuauserv = 'Manual'; UsoSvc = 'Automatic'; WaaSMedicSvc = 'Manual' };
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\wuauserv" /v Start /t REG_DWORD /d 3 /f; foreach ($name in $services.Keys) {
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\UsoSvc" /v Start /t REG_DWORD /d 2 /f; Set-Service -Name $name -StartupType $services[$name] -ErrorAction SilentlyContinue;
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc" /v Start /t REG_DWORD /d 3 /f; }
}; };
{ {
reg.exe add "HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Education" /f; reg.exe add "HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Education" /f;
+146 -60
View File
@@ -84,8 +84,8 @@
<Style x:Key="AppEntryBorderStyle" TargetType="Border"> <Style x:Key="AppEntryBorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="Gray"/> <Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="{DynamicResource AppEntryBorderThickness}"/> <Setter Property="BorderThickness" Value="{DynamicResource AppEntryBorderThickness}"/>
<Setter Property="CornerRadius" Value="2"/> <Setter Property="CornerRadius" Value="5"/>
<Setter Property="Padding" Value="{DynamicResource AppEntryMargin}"/> <Setter Property="Padding" Value="6,4"/>
<Setter Property="Width" Value="{DynamicResource AppEntryWidth}"/> <Setter Property="Width" Value="{DynamicResource AppEntryWidth}"/>
<Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Margin" Value="{DynamicResource AppEntryMargin}"/> <Setter Property="Margin" Value="{DynamicResource AppEntryMargin}"/>
@@ -100,30 +100,9 @@
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="CheckBox"> <ControlTemplate TargetType="CheckBox">
<StackPanel Orientation="Horizontal">
<Grid Width="16" Height="16" Margin="0,0,8,0">
<Border Name="CheckBoxBorder"
BorderBrush="{DynamicResource MainForegroundColor}"
Background="{DynamicResource ButtonBackgroundColor}"
BorderThickness="1"
Width="12"
Height="12"
CornerRadius="2"/>
<Path Name="CheckMark"
Stroke="{DynamicResource ToggleButtonOnColor}"
StrokeThickness="2"
Data="M 2 8 L 6 12 L 14 4"
Visibility="Collapsed"/>
</Grid>
<ContentPresenter Content="{TemplateBinding Content}" <ContentPresenter Content="{TemplateBinding Content}"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Left"/> HorizontalAlignment="Left"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> </ControlTemplate>
</Setter.Value> </Setter.Value>
</Setter> </Setter>
@@ -269,6 +248,17 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style x:Key="ComboBoxToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}" BorderThickness="0">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBox"> <Style TargetType="ComboBox">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundColor}" /> <Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundColor}" />
<Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundColor}" /> <Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundColor}" />
@@ -283,6 +273,7 @@
CornerRadius="{DynamicResource ButtonCornerRadius}" CornerRadius="{DynamicResource ButtonCornerRadius}"
Background="{TemplateBinding Background}"> Background="{TemplateBinding Background}">
<ToggleButton Name="ToggleButton" <ToggleButton Name="ToggleButton"
Style="{StaticResource ComboBoxToggleButtonStyle}"
Background="Transparent" Background="Transparent"
BorderThickness="0" BorderThickness="0"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
@@ -330,6 +321,27 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundColor}"/>
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundColor}"/>
<Setter Property="Padding" Value="6,3"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" Background="Transparent"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ComboBoxItem}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundMouseoverColor}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundSelectedColor}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="Label"> <Style TargetType="Label">
<Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/> <Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/>
<Setter Property="Background" Value="{DynamicResource LabelBackgroundColor}"/> <Setter Property="Background" Value="{DynamicResource LabelBackgroundColor}"/>
@@ -342,8 +354,7 @@
<Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/> <Setter Property="Foreground" Value="{DynamicResource LabelboxForegroundColor}"/>
<Setter Property="Background" Value="{DynamicResource LabelBackgroundColor}"/> <Setter Property="Background" Value="{DynamicResource LabelBackgroundColor}"/>
</Style> </Style>
<!-- Toggle button template x:Key="TabToggleButton" --> <Style x:Key="TabToggleButton" TargetType="{x:Type ToggleButton}">
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Margin" Value="{DynamicResource ButtonMargin}"/> <Setter Property="Margin" Value="{DynamicResource ButtonMargin}"/>
<Setter Property="Content" Value=""/> <Setter Property="Content" Value=""/>
<Setter Property="FontFamily" Value="{DynamicResource FontFamily}"/> <Setter Property="FontFamily" Value="{DynamicResource FontFamily}"/>
@@ -939,12 +950,27 @@
</MultiDataTrigger> </MultiDataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<Style x:Key="RoundedProgressBarStyle" TargetType="ProgressBar">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border CornerRadius="4" Background="{DynamicResource MainBackgroundColor}" BorderBrush="{DynamicResource MainForegroundColor}" BorderThickness="1">
<Grid ClipToBounds="True">
<Border Name="PART_Track" CornerRadius="4" Background="Transparent"/>
<Border Name="PART_Indicator" CornerRadius="4" Background="{DynamicResource ProgressBarForegroundColor}" HorizontalAlignment="Left"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources> </Window.Resources>
<Grid Background="{DynamicResource MainBackgroundColor}" ShowGridLines="False" Name="WPFMainGrid" Width="Auto" Height="Auto" HorizontalAlignment="Stretch"> <Grid Background="{DynamicResource MainBackgroundColor}" ShowGridLines="False" Name="WPFMainGrid" Width="Auto" Height="Auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -961,10 +987,10 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Navigation Buttons Panel --> <!-- Navigation Buttons Panel -->
<StackPanel Name="NavDockPanel" Orientation="Horizontal" Grid.Column="0" Margin="5,5,10,5"> <StackPanel Name="NavDockPanel" Orientation="Horizontal" Grid.Column="0" VerticalAlignment="Center" Margin="5,5,10,5">
<StackPanel Name="NavLogoPanel" Orientation="Horizontal" HorizontalAlignment="Left" Background="{DynamicResource MainBackgroundColor}" SnapsToDevicePixels="True" Margin="10,0,20,0"> <StackPanel Name="NavLogoPanel" Orientation="Horizontal" HorizontalAlignment="Left" Background="{DynamicResource MainBackgroundColor}" SnapsToDevicePixels="True" Margin="10,0,20,0">
</StackPanel> </StackPanel>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}" <ToggleButton Style="{StaticResource TabToggleButton}" Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonInstallBackgroundColor}" Foreground="white" FontWeight="Bold" Name="WPFTab1BT"> Background="{DynamicResource ButtonInstallBackgroundColor}" Foreground="white" FontWeight="Bold" Name="WPFTab1BT">
<ToggleButton.Content> <ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonInstallForegroundColor}" > <TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonInstallForegroundColor}" >
@@ -972,7 +998,7 @@
</TextBlock> </TextBlock>
</ToggleButton.Content> </ToggleButton.Content>
</ToggleButton> </ToggleButton>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}" <ToggleButton Style="{StaticResource TabToggleButton}" Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonTweaksBackgroundColor}" Foreground="{DynamicResource ButtonTweaksForegroundColor}" FontWeight="Bold" Name="WPFTab2BT"> Background="{DynamicResource ButtonTweaksBackgroundColor}" Foreground="{DynamicResource ButtonTweaksForegroundColor}" FontWeight="Bold" Name="WPFTab2BT">
<ToggleButton.Content> <ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonTweaksForegroundColor}"> <TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonTweaksForegroundColor}">
@@ -980,7 +1006,7 @@
</TextBlock> </TextBlock>
</ToggleButton.Content> </ToggleButton.Content>
</ToggleButton> </ToggleButton>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}" <ToggleButton Style="{StaticResource TabToggleButton}" Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonConfigBackgroundColor}" Foreground="{DynamicResource ButtonConfigForegroundColor}" FontWeight="Bold" Name="WPFTab3BT"> Background="{DynamicResource ButtonConfigBackgroundColor}" Foreground="{DynamicResource ButtonConfigForegroundColor}" FontWeight="Bold" Name="WPFTab3BT">
<ToggleButton.Content> <ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonConfigForegroundColor}"> <TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonConfigForegroundColor}">
@@ -988,7 +1014,7 @@
</TextBlock> </TextBlock>
</ToggleButton.Content> </ToggleButton.Content>
</ToggleButton> </ToggleButton>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}" <ToggleButton Style="{StaticResource TabToggleButton}" Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonUpdatesBackgroundColor}" Foreground="{DynamicResource ButtonUpdatesForegroundColor}" FontWeight="Bold" Name="WPFTab4BT"> Background="{DynamicResource ButtonUpdatesBackgroundColor}" Foreground="{DynamicResource ButtonUpdatesForegroundColor}" FontWeight="Bold" Name="WPFTab4BT">
<ToggleButton.Content> <ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonUpdatesForegroundColor}"> <TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonUpdatesForegroundColor}">
@@ -996,7 +1022,7 @@
</TextBlock> </TextBlock>
</ToggleButton.Content> </ToggleButton.Content>
</ToggleButton> </ToggleButton>
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="Auto" MinWidth="{DynamicResource TabButtonWidth}" <ToggleButton Style="{StaticResource TabToggleButton}" Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="Auto" MinWidth="{DynamicResource TabButtonWidth}"
Background="{DynamicResource ButtonWin11ISOBackgroundColor}" Foreground="{DynamicResource ButtonWin11ISOForegroundColor}" FontWeight="Bold" Name="WPFTab5BT"> Background="{DynamicResource ButtonWin11ISOBackgroundColor}" Foreground="{DynamicResource ButtonWin11ISOForegroundColor}" FontWeight="Bold" Name="WPFTab5BT">
<ToggleButton.Content> <ToggleButton.Content>
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonWin11ISOForegroundColor}"> <TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonWin11ISOForegroundColor}">
@@ -1004,14 +1030,6 @@
</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 -->
@@ -1021,13 +1039,12 @@
<ColumnDefinition Width="Auto"/><!-- Buttons area --> <ColumnDefinition Width="Auto"/><!-- Buttons area -->
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="5,0,0,0" Width="{DynamicResource SearchBarWidth}" Height="{DynamicResource SearchBarHeight}" VerticalAlignment="Center" HorizontalAlignment="Left"> <Border Grid.Column="0" Margin="5,0,10,0" MinWidth="120" Height="{DynamicResource SearchBarHeight}" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<Grid> <Grid>
<TextBox <TextBox
Width="{DynamicResource SearchBarWidth}"
Height="{DynamicResource SearchBarHeight}" Height="{DynamicResource SearchBarHeight}"
FontSize="{DynamicResource SearchBarTextBoxFontSize}" FontSize="{DynamicResource SearchBarTextBoxFontSize}"
VerticalAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Stretch"
BorderThickness="1" BorderThickness="1"
Name="SearchBar" Name="SearchBar"
Foreground="{DynamicResource MainForegroundColor}" Background="{DynamicResource MainBackgroundColor}" Foreground="{DynamicResource MainForegroundColor}" Background="{DynamicResource MainBackgroundColor}"
@@ -1035,6 +1052,7 @@
ToolTip="Press Ctrl-F and type app name to filter application list below. Press Esc to reset the filter"> ToolTip="Press Ctrl-F and type app name to filter application list below. Press Esc to reset the filter">
</TextBox> </TextBox>
<TextBlock <TextBlock
Name="SearchBarIcon"
VerticalAlignment="Center" HorizontalAlignment="Right" VerticalAlignment="Center" HorizontalAlignment="Right"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
Foreground="{DynamicResource ButtonBackgroundSelectedColor}" Foreground="{DynamicResource ButtonBackgroundSelectedColor}"
@@ -1044,14 +1062,14 @@
</Grid> </Grid>
</Border> </Border>
<Button Grid.Column="0" <Button Grid.Column="0"
VerticalAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Right"
Name="SearchBarClearButton" Name="SearchBarClearButton"
Style="{StaticResource SearchBarClearButtonStyle}" Style="{StaticResource SearchBarClearButtonStyle}"
Margin="213,0,0,0" Visibility="Collapsed"> Margin="0,0,20,0" Visibility="Collapsed">
</Button> </Button>
<!-- Buttons Container --> <!-- Buttons Container -->
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5,5,5,5"> <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,5,5,5">
<Button Name="ThemeButton" <Button Name="ThemeButton"
Style="{StaticResource HoverButtonStyle}" Style="{StaticResource HoverButtonStyle}"
BorderBrush="Transparent" BorderBrush="Transparent"
@@ -1059,7 +1077,7 @@
Foreground="{DynamicResource MainForegroundColor}" Foreground="{DynamicResource MainForegroundColor}"
FontSize="{DynamicResource SettingsIconFontSize}" FontSize="{DynamicResource SettingsIconFontSize}"
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}" Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
HorizontalAlignment="Right" VerticalAlignment="Top" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,2,0" Margin="0,0,2,0"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
Content="N/A" Content="N/A"
@@ -1097,7 +1115,7 @@
Foreground="{DynamicResource MainForegroundColor}" Foreground="{DynamicResource MainForegroundColor}"
FontSize="{DynamicResource SettingsIconFontSize}" FontSize="{DynamicResource SettingsIconFontSize}"
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}" Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
HorizontalAlignment="Right" VerticalAlignment="Top" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,2,0" Margin="0,0,2,0"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
Content="&#xE8D3;" Content="&#xE8D3;"
@@ -1165,7 +1183,7 @@
Foreground="{DynamicResource MainForegroundColor}" Foreground="{DynamicResource MainForegroundColor}"
FontSize="{DynamicResource SettingsIconFontSize}" FontSize="{DynamicResource SettingsIconFontSize}"
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}" Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
HorizontalAlignment="Right" VerticalAlignment="Top" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,2,0" Margin="0,0,2,0"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
Content="&#xE713;"/> Content="&#xE713;"/>
@@ -1194,24 +1212,62 @@
</Popup> </Popup>
<Button <Button
Content="&#x2212;" BorderThickness="0" Content="&#xE921;"
Style="{StaticResource HoverButtonStyle}"
BorderThickness="0"
BorderBrush="Transparent" BorderBrush="Transparent"
Background="{DynamicResource MainBackgroundColor}" Background="{DynamicResource MainBackgroundColor}"
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}" Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
HorizontalAlignment="Right" VerticalAlignment="Top" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0"
FontFamily="Segoe MDL2 Assets"
Foreground="{DynamicResource MainForegroundColor}"
FontSize="{DynamicResource CloseIconFontSize}"
ToolTip="Minimize"
AutomationProperties.Name="Minimize"
Name="WPFMinimizeButton" />
<Button
BorderThickness="0"
BorderBrush="Transparent"
Background="{DynamicResource MainBackgroundColor}"
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,0,0" Margin="0,0,0,0"
FontFamily="{DynamicResource FontFamily}" FontFamily="Segoe MDL2 Assets"
Foreground="{DynamicResource MainForegroundColor}" FontSize="{DynamicResource CloseIconFontSize}" Name="WPFMinimizeButton" /> Foreground="{DynamicResource MainForegroundColor}"
FontSize="{DynamicResource CloseIconFontSize}"
Name="WPFMaximizeButton">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource HoverButtonStyle}">
<Setter Property="Content" Value="&#xE922;"/>
<Setter Property="ToolTip" Value="Maximize"/>
<Setter Property="AutomationProperties.Name" Value="Maximize"/>
<Style.Triggers>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Maximized">
<Setter Property="Content" Value="&#xE923;"/>
<Setter Property="ToolTip" Value="Restore"/>
<Setter Property="AutomationProperties.Name" Value="Restore"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button <Button
Content="&#xD7;" BorderThickness="0" Content="&#xE8BB;"
Style="{StaticResource HoverButtonStyle}"
BorderThickness="0"
BorderBrush="Transparent" BorderBrush="Transparent"
Background="{DynamicResource MainBackgroundColor}" Background="{DynamicResource MainBackgroundColor}"
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}" Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
HorizontalAlignment="Right" VerticalAlignment="Top" HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,0,0" Margin="0"
FontFamily="{DynamicResource FontFamily}" FontFamily="Segoe MDL2 Assets"
Foreground="{DynamicResource MainForegroundColor}" FontSize="{DynamicResource CloseIconFontSize}" Name="WPFCloseButton" /> Foreground="{DynamicResource MainForegroundColor}"
FontSize="{DynamicResource CloseIconFontSize}"
ToolTip="Close"
AutomationProperties.Name="Close"
Name="WPFCloseButton" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>
@@ -1219,8 +1275,26 @@
<TabControl Name="WPFTabNav" Background="Transparent" Width="Auto" Height="Auto" BorderBrush="Transparent" BorderThickness="0" Grid.Row="2" Grid.Column="0" Padding="-1"> <TabControl Name="WPFTabNav" Background="Transparent" Width="Auto" Height="Auto" BorderBrush="Transparent" BorderThickness="0" Grid.Row="2" Grid.Column="0" Padding="-1">
<TabItem Header="Install" Visibility="Collapsed" Name="WPFTab1"> <TabItem Header="Install" Visibility="Collapsed" Name="WPFTab1">
<Grid Background="Transparent" > <Grid Background="Transparent" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0" Margin="{DynamicResource TabContentMargin}"> <!-- 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"/>
</WrapPanel>
<Grid Grid.Row="1" Margin="{DynamicResource TabContentMargin}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@@ -1258,6 +1332,7 @@
<Button Name="WPFAdvanced" Content=" Advanced " 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="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="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> </StackPanel>
</StackPanel> </StackPanel>
@@ -1728,8 +1803,9 @@
<Border Grid.Row="2" Style="{StaticResource BorderStyle}" Margin="5,15,5,5"> <Border Grid.Row="2" Style="{StaticResource BorderStyle}" Margin="5,15,5,5">
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Left"> <StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Padding="10" TextWrapping="Wrap" Foreground="{DynamicResource MainForegroundColor}"> <TextBlock Padding="10" TextWrapping="Wrap" Foreground="{DynamicResource MainForegroundColor}">
Note: Select the pre-installed Windows AppX packages you wish to remove and click 'Remove Selected'. Note: Select the Windows AppX packages you wish to install or remove.
<LineBreak/>These packages are removed for the current user and all new user profiles. <LineBreak/>Install Selected registers a local manifest when available, then falls back to the Microsoft Store.
<LineBreak/>Remove Selected removes packages for the current user and all new user profiles.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
</Border> </Border>
@@ -1738,11 +1814,21 @@
<Border Grid.Row="1" Background="{DynamicResource MainBackgroundColor}" BorderBrush="{DynamicResource BorderColor}" BorderThickness="1" CornerRadius="5" HorizontalAlignment="Stretch" Padding="10"> <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"> <WrapPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<Button Name="WPFBackToTweaks" Content="Back to Tweaks" Margin="5" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFInstallSelectedAppx" Content="Install Selected" Margin="5" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
<Button Name="WPFRemoveSelectedAppx" Content="Remove Selected" Margin="5" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/> <Button Name="WPFRemoveSelectedAppx" Content="Remove Selected" Margin="5" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
</WrapPanel> </WrapPanel>
</Border> </Border>
</Grid> </Grid>
</TabItem> </TabItem>
</TabControl> </TabControl>
<!-- Tweaks/Undo progress indicator - visible regardless of active tab -->
<Border Name="WPFTweaksProgressBar" Grid.Row="3" Background="{DynamicResource MainBackgroundColor}" Visibility="Collapsed" Padding="10,6">
<StackPanel Orientation="Vertical">
<TextBlock Name="WPFTweaksProgressLabel" Text="" Foreground="{DynamicResource MainForegroundColor}" FontSize="13" Background="Transparent" Margin="0,0,0,4"/>
<ProgressBar Name="WPFTweaksProgressValue" Height="6" Minimum="0" Maximum="100" Value="0" Style="{StaticResource RoundedProgressBarStyle}"/>
</StackPanel>
</Border>
</Grid> </Grid>
</Window> </Window>