mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-08-10 01:51:18 +10:00
Compare commits
18
Commits
15b31634e3
...
26.07.22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50be7390e5 | ||
|
|
8ab02ae756 | ||
|
|
7d1a03dcc9 | ||
|
|
5a4c9c883d | ||
|
|
5c104d02e2 | ||
|
|
06f33e88ba | ||
|
|
b9dee86694 | ||
|
|
3d1904ebbe | ||
|
|
70f1c50bdd | ||
|
|
2e5df82979 | ||
|
|
29595f90e2 | ||
|
|
9ed06e793a | ||
|
|
51cb3811dd | ||
|
|
975616b58f | ||
|
|
11783c75a8 | ||
|
|
13a18eeeb2 | ||
|
|
822003d87f | ||
|
|
c8c3c7869f |
+88
-114
@@ -1,149 +1,123 @@
|
|||||||
## Contributing Code
|
# How to Contribute?
|
||||||
|
|
||||||
### Before You Start
|
## Testing
|
||||||
|
|
||||||
- Keep each pull request focused on a single feature or fix.
|
* Test the latest changes to WinUtil by running the pre-release and reporting issues you are encountering to help us continually improve WinUtil!
|
||||||
- Avoid unnecessary formatting changes or large unrelated edits.
|
|
||||||
- Document what changed and why in your PR description.
|
|
||||||
|
|
||||||
---
|
#### **Run the latest pre-release**
|
||||||
|
```ps1
|
||||||
|
irm https://christitus.com/windev | iex
|
||||||
|
```
|
||||||
|
|
||||||
## Basic Git Workflow
|
!!! bug "Keep in mind"
|
||||||
|
|
||||||
### 1. Fork the Repository
|
This is a pre-release and should be treated as such. It exists for developers to test the utility and report or fix bugs before they get added to the stable release. Don't use it in production!
|
||||||
|
|
||||||
Go to the ChrisTitusTech/winutil repository on GitHub and click the Fork button in the top right corner.
|
## Issues
|
||||||
|
|
||||||
<img width="171" height="50" alt="{650A4723-F38A-44A4-9820-D232BC87C8A0}" src="https://github.com/user-attachments/assets/a214f27c-2fee-444a-920f-d87b14f5896f" />
|
* If you encounter any challenges or problems with the script, I kindly request that you submit them via the "Issues" tab on the GitHub repository. By filling out the provided template, you can provide specific details about the issue, allowing me (and others in the community) to promptly address any bugs or consider feature requests.
|
||||||
|
|
||||||
---
|
## Contribute Code
|
||||||
|
|
||||||
### 2. Clone Your Fork
|
* Pull requests are now handled directly on the **MAIN branch**. This was done since we can now select specific releases to launch via releases in GitHub.
|
||||||
|
|
||||||
```powershell
|
* If you're doing code changes, then you can submit a PR to the `main` branch.
|
||||||
git clone https://github.com/YOUR_USERNAME/winutil.git
|
|
||||||
cd winutil
|
!!! warning "Important"
|
||||||
|
|
||||||
|
Do not use a code formatter, make massive amounts of line changes, or make multiple feature changes. EACH FEATURE CHANGE SHOULD BE IT'S OWN PULL REQUEST!
|
||||||
|
|
||||||
|
Do not open a pull request that adds support for other languages to WinUtil for now, until we decide how we want to move forward with language support.
|
||||||
|
|
||||||
|
* When creating pull requests, it is essential to thoroughly document all changes made. This includes, but is not limited to, documenting any additions made to the `tweaks` section and corresponding `undo tweak`, so users are able to remove the newly added tweaks if necessary, and comprehensive documentation is required for all code changes. Document your changes and briefly explain why you made your changes in your Pull Request Description. Failure to adhere to this format may result in the denial of the pull request. Additionally, any code lacking sufficient documentation may also be denied.
|
||||||
|
|
||||||
|
* By following these guidelines, we can maintain a high standard of quality and ensure that the codebase remains organized and well-documented.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
When creating a function, please include "WPF" or "WinUtil" in the file name so it can be loaded into the runspace.
|
||||||
|
|
||||||
|
## Walk through
|
||||||
|
|
||||||
|
* This is a guide for beginners. If you are still having issues, look at the following official GitHub documentation:
|
||||||
|
* [Commit through WEB](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits)
|
||||||
|
* [Commit through GitHub Desktop](https://docs.github.com/en/desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project-in-github-desktop#about-commits)
|
||||||
|
* [Create a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)
|
||||||
|
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
``` mermaid
|
||||||
|
%%{init: {"flowchart": {"curve": "cardinal"}} }%%
|
||||||
|
graph TD
|
||||||
|
A[Fork Project] --> B[Clone Repository];
|
||||||
|
B --> C[Create New Branch];
|
||||||
|
C --> D[Make Changes];
|
||||||
|
D --> G[Test Changes];
|
||||||
|
G --> H{Tests Passed?};
|
||||||
|
H -->|Yes| E[Commit Changes];
|
||||||
|
H -->|No| J[Fix Issues];
|
||||||
|
J --> G;
|
||||||
|
E --> F[Push Branch];
|
||||||
|
F --> K[Create Pull Request];
|
||||||
|
K --> L[Fill out PR template];
|
||||||
|
classDef default stroke:#333,stroke-width:4px,font-size:12pt;
|
||||||
```
|
```
|
||||||
|
!!! info
|
||||||
|
|
||||||
---
|
This is a diagram to guide you through the process. It may vary depending on the type of change you're making.
|
||||||
|
|
||||||
### 3. Create a Branch
|
### Fork the Repo
|
||||||
|
* Fork the WinUtil Repository [here](https://github.com/ChrisTitusTech/winutil) to create a copy that will be available in your repository list.
|
||||||
|
|
||||||
Never work directly on `main`.
|

|
||||||
|
|
||||||
Create a branch related to your change:
|

|
||||||
|
|
||||||
```powershell
|
### Clone the Fork
|
||||||
git checkout -b feature-name
|
!!! tip
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
While you can make your changes directly through the Web, we recommend cloning the repo to your device using the application GitHub Desktop (available in WinUtil) to test your fork easily.
|
||||||
|
|
||||||
```powershell
|
* Install GitHub Desktop if it is not already installed.
|
||||||
git checkout -b add-firefox-tweak
|
* Log in using the same GitHub account you used to fork WinUtil.
|
||||||
```
|
* Choose the fork under "Your Repositories" and press "clone {repo name}"
|
||||||
|
* Create a new branch and name it something relatable to your changes.
|
||||||
|
|
||||||
---
|
* Now you can modify WinUtil to your liking using your preferred text editor.
|
||||||
|
|
||||||
### 4. Edit the Code
|
|
||||||
|
|
||||||
Open the project in your preferred text editor and make your changes.
|
### Testing your changes
|
||||||
|
|
||||||
Keep changes small and focused.
|
* To test to see if your changes work as intended, run the following commands in a PowerShell terminal as admin:
|
||||||
|
|
||||||
---
|
* Change the directory where you are running the commands to the forked project.
|
||||||
|
* `cd {path to the folder with the compile.ps1}`
|
||||||
|
* Run the following command to compile and run WinUtil:
|
||||||
|
* `.\Compile.ps1 -run`
|
||||||
|
|
||||||
### 5. Test Your Changes
|

|
||||||
|
|
||||||
Open Powershell as Administrator.
|
|
||||||
|
|
||||||
Go to the project folder:
|
Open PowerShell as Administrator.
|
||||||
|
|
||||||
```powershell
|
* After seeing that your changes work properly, feel free to commit the changes to the repository and make a PR. For help on that, follow the documentation below.
|
||||||
cd path\to\winutil
|
|
||||||
```
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```powershell
|
### Committing the changes
|
||||||
.\Compile.ps1 -Run
|
* Before committing your changes, please discard changes made to the `winutil.ps1` file, like the following:
|
||||||
```
|
|
||||||
|
|
||||||
Verify:
|

|
||||||
|
|
||||||
- WinUtil launches correctly
|
* Now, commit your changes once you are happy with the result.
|
||||||
- Your feature works
|
|
||||||
- Nothing else breaks
|
|
||||||
|
|
||||||
If something fails, fix it before committing.
|

|
||||||
|
|
||||||
---
|
* Push the changes to upload them to your fork on github.com.
|
||||||
|
|
||||||
### 6. Review Your Changes
|

|
||||||
|
|
||||||
Check what changed:
|
### Making a PR
|
||||||
|
* To make a PR on your repo under a new branch linking to the main branch, a button will show and say Preview and Create pull request. Click that button and fill in all the information that is provided on the template. Once all the information is filled in correctly, check your PR to make sure there is no WinUtil.ps1 file attached to the PR. Once everything is good, make the PR and wait for Chris (the maintainer) to accept or deny your PR. Once it is accepted by Chris, you will be able to see your changes in the "/windev" build.
|
||||||
```powershell
|
* If you do not see your feature in the main "/win" build, that is fine. All new changes go into the /windev build to make sure everything is working OK before going fully public.
|
||||||
git status
|
* Congratulations! You just submitted your first PR. Thank you so much for contributing to WinUtil.
|
||||||
```
|
|
||||||
|
|
||||||
Review the diff:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
git diff
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure you did not accidentally modify unrelated files.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 7. Commit Your Changes
|
|
||||||
|
|
||||||
Stage files:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
git add .
|
|
||||||
```
|
|
||||||
|
|
||||||
Commit them:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
git commit -m "Add feature description"
|
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
git commit -m "Add Firefox package tweak"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 8. Push Your Branch
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
git push origin branch-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
git push origin add-firefox-tweak
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 9. Open a Pull Request
|
|
||||||
|
|
||||||
Go to your fork on GitHub.
|
|
||||||
|
|
||||||
GitHub will show a button to create a pull request.
|
|
||||||
<img width="1009" height="71" alt="{C8C6A3CC-79D4-44FD-A54C-4C5717F12730}" src="https://github.com/user-attachments/assets/0419d193-d4e7-47c0-87cf-b986742201a0" />
|
|
||||||
|
|
||||||
Before submitting:
|
|
||||||
|
|
||||||
- Explain what changed
|
|
||||||
- Explain why you changed it
|
|
||||||
- Make sure unrelated files are not included
|
|
||||||
|
|
||||||
Once submitted, maintainers will review your PR.
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Auto-merge Docs PRs
|
name: Auto-merge Automated Update PRs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -8,7 +8,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
auto-merge:
|
auto-merge:
|
||||||
if: github.event.pull_request.head.ref == 'docs-update' && (github.event.pull_request.user.login == 'ChrisTitusTech' || github.event.pull_request.user.login == 'github-actions[bot]')
|
if: (github.event.pull_request.head.ref == 'docs-update' || github.event.pull_request.head.ref == 'sponsors-update') && (github.event.pull_request.user.login == 'ChrisTitusTech' || github.event.pull_request.user.login == 'github-actions[bot]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|||||||
@@ -109,6 +109,21 @@ jobs:
|
|||||||
config-name: release-drafter.yml
|
config-name: release-drafter.yml
|
||||||
version: ${{ env.VERSION }}
|
version: ${{ env.VERSION }}
|
||||||
|
|
||||||
|
- name: Remove Automated Updates from Release Notes
|
||||||
|
id: filter_notes
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
RELEASE_NOTES: ${{ steps.generate_notes.outputs.body }}
|
||||||
|
run: |
|
||||||
|
$filteredNotes = ($env:RELEASE_NOTES -split '\r?\n' | Where-Object {
|
||||||
|
$_ -notmatch '(?i)Update (Generated Dev Docs|Sponsors)'
|
||||||
|
}) -join "`n"
|
||||||
|
|
||||||
|
$delimiter = [guid]::NewGuid().ToString()
|
||||||
|
"body<<$delimiter" >> $env:GITHUB_OUTPUT
|
||||||
|
$filteredNotes >> $env:GITHUB_OUTPUT
|
||||||
|
$delimiter >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create and Upload Release
|
- name: Create and Upload Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v3
|
uses: softprops/action-gh-release@v3
|
||||||
@@ -116,7 +131,7 @@ jobs:
|
|||||||
tag_name: ${{ env.VERSION }}
|
tag_name: ${{ env.VERSION }}
|
||||||
name: Pre-Release ${{ env.VERSION }}
|
name: Pre-Release ${{ env.VERSION }}
|
||||||
body: |
|
body: |
|
||||||
${{ steps.generate_notes.outputs.body }}
|
${{ steps.filter_notes.outputs.body }}
|
||||||
|
|
||||||

|

|
||||||
append_body: false
|
append_body: false
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v8
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.AUTO_MERGE }}
|
||||||
commit-message: 'Update sponsors in README'
|
commit-message: 'Update sponsors in README'
|
||||||
title: 'chore: Update Sponsors README'
|
title: 'chore: Update Sponsors README'
|
||||||
body: 'Automated update of sponsors section'
|
body: 'Automated update of sponsors section'
|
||||||
|
|||||||
@@ -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/ysaito8015"><img src="https://github.com/ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https://github.com/andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/johanwildeboer"><img src="https://github.com/johanwildeboer.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/lukas346"><img src="https://github.com/lukas346.png" width="60px" alt="User avatar: Wook" /></a><a href="https://github.com/tsv31"><img src="https://github.com/tsv31.png" width="60px" alt="User avatar: Sorin" /></a><!-- sponsors -->
|
<!-- sponsors --><a href="https://github.com/ysaito8015"><img src="https://github.com/ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https://github.com/andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/johanwildeboer"><img src="https://github.com/johanwildeboer.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/lukas346"><img src="https://github.com/lukas346.png" width="60px" alt="User avatar: Wook" /></a><a href="https://github.com/tsv31"><img src="https://github.com/tsv31.png" width="60px" alt="User avatar: Sorin" /></a><a href="https://github.com/seanh1995"><img src="https://github.com/seanh1995.png" width="60px" alt="User avatar: Sean (ANGRYxScotsman)" /></a><!-- sponsors -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+60
-30
@@ -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",
|
||||||
|
|||||||
+2
-6
@@ -25,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",
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
"AppInstallUnselectedColor": "#F7F7F7",
|
"AppInstallUnselectedColor": "#F7F7F7",
|
||||||
"AppInstallHighlightedColor": "#CFCFCF",
|
"AppInstallHighlightedColor": "#CFCFCF",
|
||||||
"AppInstallSelectedColor": "#C2C2C2",
|
"AppInstallSelectedColor": "#C2C2C2",
|
||||||
"AppInstallOverlayBackgroundColor": "#6A6D72",
|
|
||||||
"ComboBoxForegroundColor": "#232629",
|
"ComboBoxForegroundColor": "#232629",
|
||||||
"ComboBoxBackgroundColor": "#F7F7F7",
|
"ComboBoxBackgroundColor": "#F7F7F7",
|
||||||
"LabelboxForegroundColor": "#232629",
|
"LabelboxForegroundColor": "#232629",
|
||||||
@@ -60,7 +59,6 @@
|
|||||||
"ScrollBarDraggingColor": "#6A6D72",
|
"ScrollBarDraggingColor": "#6A6D72",
|
||||||
"ProgressBarForegroundColor": "#2E77FF",
|
"ProgressBarForegroundColor": "#2E77FF",
|
||||||
"ProgressBarBackgroundColor": "Transparent",
|
"ProgressBarBackgroundColor": "Transparent",
|
||||||
"ProgressBarTextColor": "#232629",
|
|
||||||
"ButtonInstallBackgroundColor": "#F7F7F7",
|
"ButtonInstallBackgroundColor": "#F7F7F7",
|
||||||
"ButtonTweaksBackgroundColor": "#F7F7F7",
|
"ButtonTweaksBackgroundColor": "#F7F7F7",
|
||||||
"ButtonConfigBackgroundColor": "#F7F7F7",
|
"ButtonConfigBackgroundColor": "#F7F7F7",
|
||||||
@@ -88,7 +86,6 @@
|
|||||||
"AppInstallUnselectedColor": "#232629",
|
"AppInstallUnselectedColor": "#232629",
|
||||||
"AppInstallHighlightedColor": "#3C3C3C",
|
"AppInstallHighlightedColor": "#3C3C3C",
|
||||||
"AppInstallSelectedColor": "#4C4C4C",
|
"AppInstallSelectedColor": "#4C4C4C",
|
||||||
"AppInstallOverlayBackgroundColor": "#2E3135",
|
|
||||||
"ComboBoxForegroundColor": "#F7F7F7",
|
"ComboBoxForegroundColor": "#F7F7F7",
|
||||||
"ComboBoxBackgroundColor": "#1E3747",
|
"ComboBoxBackgroundColor": "#1E3747",
|
||||||
"LabelboxForegroundColor": "#5BDCFF",
|
"LabelboxForegroundColor": "#5BDCFF",
|
||||||
@@ -100,9 +97,8 @@
|
|||||||
"ScrollBarBackgroundColor": "#2E3135",
|
"ScrollBarBackgroundColor": "#2E3135",
|
||||||
"ScrollBarHoverColor": "#3B4252",
|
"ScrollBarHoverColor": "#3B4252",
|
||||||
"ScrollBarDraggingColor": "#5E81AC",
|
"ScrollBarDraggingColor": "#5E81AC",
|
||||||
"ProgressBarForegroundColor": "#222222",
|
"ProgressBarForegroundColor": "#6EFF72",
|
||||||
"ProgressBarBackgroundColor": "Transparent",
|
"ProgressBarBackgroundColor": "Transparent",
|
||||||
"ProgressBarTextColor": "#232629",
|
|
||||||
"ButtonInstallBackgroundColor": "#222222",
|
"ButtonInstallBackgroundColor": "#222222",
|
||||||
"ButtonTweaksBackgroundColor": "#333333",
|
"ButtonTweaksBackgroundColor": "#333333",
|
||||||
"ButtonConfigBackgroundColor": "#444444",
|
"ButtonConfigBackgroundColor": "#444444",
|
||||||
|
|||||||
+1
-1
@@ -650,7 +650,7 @@
|
|||||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||||
|
|
||||||
Write-Host \"Uninstalling OneDrive...\"
|
Write-Host \"Uninstalling OneDrive...\"
|
||||||
Start-Process '$Env:SystemRoot\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
Start-Process -FilePath (Join-Path $Env:SystemRoot \"System32\\OneDriveSetup.exe\") -ArgumentList '/uninstall' -Wait
|
||||||
|
|
||||||
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
||||||
Write-Host \"Removing leftover OneDrive Files...\"
|
Write-Host \"Removing leftover OneDrive Files...\"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
+88
-119
@@ -2,152 +2,121 @@
|
|||||||
toc: true
|
toc: true
|
||||||
---
|
---
|
||||||
|
|
||||||
## Contributing Code
|
# How to Contribute?
|
||||||
|
|
||||||
### Before You Start
|
## Testing
|
||||||
|
|
||||||
- Keep each pull request focused on a single feature or fix.
|
* Test the latest changes to WinUtil by running the pre-release and reporting issues you are encountering to help us continually improve WinUtil!
|
||||||
- Avoid unnecessary formatting changes or large unrelated edits.
|
|
||||||
- Document what changed and why in your PR description.
|
|
||||||
|
|
||||||
---
|
#### Run the latest pre-release
|
||||||
|
|
||||||
## Basic Git Workflow
|
```ps1
|
||||||
|
irm https://christitus.com/windev | iex
|
||||||
### 1. Fork the Repository
|
|
||||||
|
|
||||||
Go to the ChrisTitusTech/winutil repository on GitHub and click the Fork button in the top right corner.
|
|
||||||
|
|
||||||
<img width="171" height="50" alt="{650A4723-F38A-44A4-9820-D232BC87C8A0}" src="https://github.com/user-attachments/assets/a214f27c-2fee-444a-920f-d87b14f5896f" />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Clone Your Fork
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/YOUR_USERNAME/winutil.git
|
|
||||||
cd winutil
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
> [!IMPORTANT]
|
||||||
|
> **Keep in mind** That this is a pre-release and should be treated as such. It exists for developers to test the utility and report or fix bugs before they get added to the stable release. Don't use it in production!
|
||||||
|
|
||||||
### 3. Create a Branch
|
## Issues
|
||||||
|
|
||||||
Never work directly on `main`.
|
* If you encounter any challenges or problems with the script, I kindly request that you submit them via the "Issues" tab on the GitHub repository. By filling out the provided template, you can provide specific details about the issue, allowing me (and others in the community) to promptly address any bugs or consider feature requests.
|
||||||
|
|
||||||
Create a branch related to your change:
|
## Contribute Code
|
||||||
|
|
||||||
```bash
|
* Pull requests are now handled directly on the **MAIN branch**. This was done since we can now select specific releases to launch via releases in GitHub.
|
||||||
git checkout -b feature-name
|
|
||||||
|
* If you're doing code changes, then you can submit a PR to the `main` branch.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Do not use a code formatter, make massive amounts of line changes, or make multiple feature changes. EACH FEATURE CHANGE SHOULD BE IT'S OWN PULL REQUEST!
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Do not open a pull request that adds support for other languages to WinUtil for now, until we decide how we want to move forward with language support.
|
||||||
|
|
||||||
|
* When creating pull requests, it is essential to thoroughly document all changes made. This includes, but is not limited to, documenting any additions made to the `tweaks` section and corresponding `undo tweak`, so users are able to remove the newly added tweaks if necessary, and comprehensive documentation is required for all code changes. Document your changes and briefly explain why you made your changes in your Pull Request Description. Failure to adhere to this format may result in the denial of the pull request. Additionally, any code lacking sufficient documentation may also be denied.
|
||||||
|
|
||||||
|
* By following these guidelines, we can maintain a high standard of quality and ensure that the codebase remains organized and well-documented.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> When creating a function, please include "WPF" or "WinUtil" in the file name so it can be loaded into the runspace.
|
||||||
|
|
||||||
|
## Walk through
|
||||||
|
|
||||||
|
* This is a guide for beginners. If you are still having issues, look at the following official GitHub documentation:
|
||||||
|
* [Commit through WEB](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits)
|
||||||
|
* [Commit through GitHub Desktop](https://docs.github.com/en/desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project-in-github-desktop#about-commits)
|
||||||
|
* [Create a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)
|
||||||
|
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
%%{init: {"flowchart": {"curve": "cardinal"}} }%%
|
||||||
|
graph TD
|
||||||
|
A[Fork Project] --> B[Clone Repository];
|
||||||
|
B --> C[Create New Branch];
|
||||||
|
C --> D[Make Changes];
|
||||||
|
D --> G[Test Changes];
|
||||||
|
G --> H{Tests Passed?};
|
||||||
|
H -->|Yes| E[Commit Changes];
|
||||||
|
H -->|No| J[Fix Issues];
|
||||||
|
J --> G;
|
||||||
|
E --> F[Push Branch];
|
||||||
|
F --> K[Create Pull Request];
|
||||||
|
K --> L[Fill out PR template];
|
||||||
|
classDef default stroke:#333,stroke-width:4px,font-size:12pt;
|
||||||
```
|
```
|
||||||
|
|
||||||
Example:
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> This is a diagram to guide you through the process. It may vary depending on the type of change you're making.
|
||||||
|
|
||||||
```bash
|
### Fork the Repo
|
||||||
git checkout -b add-firefox-tweak
|
* Fork the WinUtil Repository [here](https://github.com/ChrisTitusTech/winutil) to create a copy that will be available in your repository list.
|
||||||
```
|
|
||||||
|
|
||||||
---
|
{{< image src="images/Fork-Button" alt="Fork Image" >}}
|
||||||
|
|
||||||
### 4. Edit the Code
|
### Clone the Fork
|
||||||
|
> [!TIP]
|
||||||
|
>
|
||||||
|
> While you can make your changes directly through the Web, we recommend cloning the repo to your device using the application GitHub Desktop (available in WinUtil) to test your fork easily.
|
||||||
|
|
||||||
Open the project in your preferred text editor and make your changes.
|
* Install GitHub Desktop if it is not already installed.
|
||||||
|
* Log in using the same GitHub account you used to fork WinUtil.
|
||||||
|
* Choose the fork under "Your Repositories" and press "clone {repo name}"
|
||||||
|
* Create a new branch and name it something relatable to your changes.
|
||||||
|
|
||||||
Keep changes small and focused.
|
* Now you can modify WinUtil to your liking using your preferred text editor.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. Test Your Changes
|
### Testing your changes
|
||||||
|
|
||||||
Open PowerShell as Administrator.
|
* To test to see if your changes work as intended, run the following commands in a PowerShell terminal as admin:
|
||||||
|
|
||||||
Go to the project folder:
|
* Change the directory where you are running the commands to the forked project.
|
||||||
|
* `cd {path to the folder with the compile.ps1}`
|
||||||
|
* Run the following command to compile and run WinUtil:
|
||||||
|
* `.\Compile.ps1 -run`
|
||||||
|
|
||||||
```powershell
|
{{< image src="images/Complie" alt="Compile" >}}
|
||||||
cd path\to\winutil
|
|
||||||
```
|
|
||||||
|
|
||||||
Run:
|
* After seeing that your changes work properly, feel free to commit the changes to the repository and make a PR. For help on that, follow the documentation below.
|
||||||
|
|
||||||
```powershell
|
### Committing the changes
|
||||||
.\Compile.ps1 -Run
|
* Before committing your changes, please discard changes made to the `winutil.ps1` file, like the following:
|
||||||
```
|
|
||||||
|
|
||||||
Verify:
|
{{< image src="images/Discard-GHD" alt="Push Commit Image" >}}
|
||||||
|
|
||||||
- WinUtil launches correctly
|
* Now, commit your changes once you are happy with the result.
|
||||||
- Your feature works
|
|
||||||
- Nothing else breaks
|
|
||||||
|
|
||||||
If something fails, fix it before committing.
|
{{< image src="images/Commit-GHD" alt="Commit Image" >}}
|
||||||
|
|
||||||
---
|
* Push the changes to upload them to your fork on github.com.
|
||||||
|
|
||||||
### 6. Review Your Changes
|
{{< image src="images/Push-Commit" alt="Push Commit Image" >}}
|
||||||
|
|
||||||
Check what changed:
|
### Making a PR
|
||||||
|
* To make a PR on your repo under a new branch linking to the main branch, a button will show and say Preview and Create pull request. Click that button and fill in all the information that is provided on the template. Once all the information is filled in correctly, check your PR to make sure there is no WinUtil.ps1 file attached to the PR. Once everything is good, make the PR and wait for Chris (the maintainer) to accept or deny your PR. Once it is accepted by Chris, you will be able to see your changes in the "/windev" build.
|
||||||
```bash
|
* If you do not see your feature in the main "/win" build, that is fine. All new changes go into the /windev build to make sure everything is working OK before going fully public.
|
||||||
git status
|
* Congratulations! You just submitted your first PR. Thank you so much for contributing to WinUtil.
|
||||||
```
|
|
||||||
|
|
||||||
Review the diff:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git diff
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure you did not accidentally modify unrelated files.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 7. Commit Your Changes
|
|
||||||
|
|
||||||
Stage files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .
|
|
||||||
```
|
|
||||||
|
|
||||||
Commit them:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git commit -m "Add feature description"
|
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git commit -m "Add Firefox package tweak"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 8. Push Your Branch
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git push origin branch-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git push origin add-firefox-tweak
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 9. Open a Pull Request
|
|
||||||
|
|
||||||
Go to your fork on GitHub.
|
|
||||||
|
|
||||||
GitHub will show a button to create a pull request.
|
|
||||||
<img width="1009" height="71" alt="{C8C6A3CC-79D4-44FD-A54C-4C5717F12730}" src="https://github.com/user-attachments/assets/0419d193-d4e7-47c0-87cf-b986742201a0" />
|
|
||||||
|
|
||||||
Before submitting:
|
|
||||||
|
|
||||||
- Explain what changed
|
|
||||||
- Explain why you changed it
|
|
||||||
- Make sure unrelated files are not included
|
|
||||||
|
|
||||||
Once submitted, maintainers will review your PR.
|
|
||||||
|
|||||||
@@ -5,15 +5,53 @@ description: ""
|
|||||||
|
|
||||||
```powershell {filename="functions/public/Invoke-WPFOOSU.ps1",linenos=inline,linenostart=1}
|
```powershell {filename="functions/public/Invoke-WPFOOSU.ps1",linenos=inline,linenostart=1}
|
||||||
function Invoke-WPFOOSU {
|
function Invoke-WPFOOSU {
|
||||||
try {
|
if ($sync.ProcessRunning) {
|
||||||
$ProgressPreference = 'SilentlyContinue'
|
Show-WinUtilMessage -Message "Another process is currently running." -Title "WinUtil" -Button "OK" -Icon "Warning"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Invoke-WebRequest -Uri https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe -OutFile "$winutildir\ooshutup10.exe"
|
$downloadPath = Join-Path $sync.winutildir "ooshutup10.exe"
|
||||||
Start-Process -FilePath "$winutildir\ooshutup10.exe"
|
$sync.ProcessRunning = $true
|
||||||
|
|
||||||
$ProgressPreference = 'Continue'
|
Invoke-WPFRunspace -ParameterList @(,("downloadPath", $downloadPath)) -ScriptBlock {
|
||||||
} catch {
|
param($downloadPath)
|
||||||
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
|
||||||
|
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
|
||||||
|
|
||||||
|
try {
|
||||||
|
Write-WinUtilLog -Component "OOSU" -Message "Downloading O&O ShutUp10++."
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ (0%)" -Percent 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Save-WinUtilFile -Uri "https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe" -DestinationPath $downloadPath -ProgressCallback {
|
||||||
|
param($percent)
|
||||||
|
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ ($percent%)" -Percent $percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Launching O&O ShutUp10++" -Percent 100
|
||||||
|
}
|
||||||
|
Start-Process -FilePath $downloadPath
|
||||||
|
|
||||||
|
Write-WinUtilLog -Component "OOSU" -Message "O&O ShutUp10++ launched."
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ launched" -Percent 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-WinUtilLog -Level "ERROR" -Component "OOSU" -Message "O&O ShutUp10++ download failed: $($_.Exception.Message)"
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ download failed" -Percent 100
|
||||||
|
}
|
||||||
|
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
$sync.ProcessRunning = $false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ description: ""
|
|||||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||||
|
|
||||||
Write-Host \"Uninstalling OneDrive...\"
|
Write-Host \"Uninstalling OneDrive...\"
|
||||||
Start-Process '$Env:SystemRoot\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
Start-Process -FilePath (Join-Path $Env:SystemRoot \"System32\\OneDriveSetup.exe\") -ArgumentList '/uninstall' -Wait
|
||||||
|
|
||||||
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
||||||
Write-Host \"Removing leftover OneDrive Files...\"
|
Write-Host \"Removing leftover OneDrive Files...\"
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ For a better Windows experience with minimal risk:
|
|||||||
|
|
||||||
1. Check multiple application boxes
|
1. Check multiple application boxes
|
||||||
2. All checked apps will install in sequence
|
2. All checked apps will install in sequence
|
||||||
3. Progress is shown in the bottom panel
|
3. Install and uninstall progress is shown in the window-level bottom panel, including the current package or package-manager batch and overall completion
|
||||||
|
|
||||||
### Applying Tweaks
|
### Applying Tweaks
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -9,28 +9,30 @@ WinUtil provides three update modes so you can choose how aggressively Windows U
|
|||||||
|
|
||||||
Changing modes adjusts system-wide Windows Update behavior. After switching modes, give Windows a moment to apply the policy and plan for a restart if the new state does not appear immediately.
|
Changing modes adjusts system-wide Windows Update behavior. After switching modes, give Windows a moment to apply the policy and plan for a restart if the new state does not appear immediately.
|
||||||
|
|
||||||
{{< image src="images/updates-tab-new" alt="Updates tab in WinUtil" >}}
|
- **Recommended**: Prioritizes stability while still receiving security updates
|
||||||
|
- **Windows Default**: Restores standard Windows Update behavior
|
||||||
|
- **Disable Updates**: Blocks Windows Update and should only be used with extreme caution
|
||||||
|
|
||||||
- **Default (Out of the Box) Settings**: Restores standard Windows Update behavior
|
### Windows Default
|
||||||
- **Security (Recommended) Settings**: Prioritizes stability while still receiving security updates
|
|
||||||
- **Disable ALL Updates**: Turns off Windows Update entirely and should only be used with extreme caution
|
|
||||||
|
|
||||||
### Default (Out of Box) Settings
|
- **What it does**: Removes Windows Update policies managed by WinUtil, restores update service startup settings, and re-enables update scheduled tasks.
|
||||||
|
|
||||||
- **What it does**: Restores the default Windows Update configuration.
|
|
||||||
- **Best for**: Systems where you want Windows to manage updates normally.
|
- **Best for**: Systems where you want Windows to manage updates normally.
|
||||||
- **Notes**: This removes custom update settings previously applied by WinUtil. If update errors continue, use the reset option in the **Config** tab to restore Microsoft Update services to their default state.
|
- **Notes**: Only values managed by WinUtil are removed; other Windows Update policies are left in place. If update errors continue, use the reset option in the **Config** tab to repair Microsoft Update components.
|
||||||
|
|
||||||
### Security (Recommended) Settings
|
### Recommended
|
||||||
|
|
||||||
- **What it does**: Applies a more conservative update strategy designed for most users.
|
- **What it does**: Applies a more conservative update strategy designed for most users.
|
||||||
- **Feature updates**: Delayed by **365 days** to reduce the chance of disruption from major Windows changes.
|
- **Feature updates**: Delayed by **365 days** to reduce the chance of disruption from major Windows changes.
|
||||||
- **Security updates**: Delayed by **4 days** to allow time for early issues to surface while still keeping the system protected.
|
- **Quality updates**: Delayed by **4 days** to allow time for early issues to surface while still keeping the system protected.
|
||||||
|
- **Drivers**: Excluded from Windows quality updates.
|
||||||
|
- **Restarts**: Scheduled updates do not automatically restart Windows while a user is signed in. A restart explicitly scheduled by a user still takes precedence.
|
||||||
|
- **Availability**: Update deferral policies apply to Windows Pro, Enterprise, and Education editions.
|
||||||
- **Why use it**: This mode offers the best balance between security and stability, which is why it is the recommended option for most PCs.
|
- **Why use it**: This mode offers the best balance between security and stability, which is why it is the recommended option for most PCs.
|
||||||
|
|
||||||
### Disable ALL Updates (NOT RECOMMENDED!)
|
### Disable Updates (NOT RECOMMENDED!)
|
||||||
|
|
||||||
- **What it does**: Disables all Windows updates.
|
- **What it does**: Disables automatic update policy, stops and disables update services, disables update scheduled tasks, and clears downloaded update files.
|
||||||
- **Best for**: Highly controlled or special-purpose systems where updates must remain off temporarily.
|
- **Best for**: Highly controlled or special-purpose systems where updates must remain off temporarily.
|
||||||
- **Warning**: This leaves the system without security patches and significantly increases security risk.
|
- **Warning**: This leaves the system without security patches and significantly increases security risk.
|
||||||
|
- **Notes**: Windows servicing can restore update components in some circumstances. Use **Restore Defaults** when you are ready to receive updates again.
|
||||||
- **Recommendation**: Avoid this mode unless you fully understand the tradeoffs and have a specific reason to use it.
|
- **Recommendation**: Avoid this mode unless you fully understand the tradeoffs and have a specific reason to use it.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
function Hide-WPFInstallAppBusy {
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Hides the busy overlay in the install app area of the WPF form.
|
|
||||||
This is used to indicate that an install or uninstall has finished.
|
|
||||||
#>
|
|
||||||
Invoke-WPFUIThread -ScriptBlock {
|
|
||||||
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed
|
|
||||||
$sync.InstallAppAreaBorder.IsEnabled = $true
|
|
||||||
$sync.InstallAppAreaScrollViewer.Effect.Radius = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,6 @@
|
|||||||
This is used as the parent object for all category and app entries on the install tab
|
This is used as the parent object for all category and app entries on the install tab
|
||||||
Used to as part of the Install Tab UI generation
|
Used to as part of the Install Tab UI generation
|
||||||
|
|
||||||
Also creates an overlay with a progress bar and text to indicate that an install or uninstall is in progress
|
|
||||||
|
|
||||||
.PARAMETER TargetElement
|
.PARAMETER TargetElement
|
||||||
The element to which the AppArea should be added
|
The element to which the AppArea should be added
|
||||||
|
|
||||||
@@ -19,22 +17,14 @@
|
|||||||
$Border = New-Object Windows.Controls.Border
|
$Border = New-Object Windows.Controls.Border
|
||||||
$Border.VerticalAlignment = "Stretch"
|
$Border.VerticalAlignment = "Stretch"
|
||||||
$Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle")
|
$Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle")
|
||||||
$sync.InstallAppAreaBorder = $Border
|
|
||||||
|
|
||||||
# Add a ScrollViewer, because the ItemsControl does not support scrolling by itself
|
# Add a ScrollViewer, because the ItemsControl does not support scrolling by itself
|
||||||
$scrollViewer = New-Object Windows.Controls.ScrollViewer
|
$scrollViewer = New-Object Windows.Controls.ScrollViewer
|
||||||
$scrollViewer.VerticalScrollBarVisibility = 'Auto'
|
$scrollViewer.VerticalScrollBarVisibility = 'Auto'
|
||||||
$scrollViewer.HorizontalAlignment = 'Stretch'
|
$scrollViewer.HorizontalAlignment = 'Stretch'
|
||||||
$scrollViewer.VerticalAlignment = 'Stretch'
|
$scrollViewer.VerticalAlignment = 'Stretch'
|
||||||
$scrollViewer.CanContentScroll = $true
|
$scrollViewer.CanContentScroll = $true
|
||||||
$sync.InstallAppAreaScrollViewer = $scrollViewer
|
|
||||||
$Border.Child = $scrollViewer
|
$Border.Child = $scrollViewer
|
||||||
|
|
||||||
# Initialize the Blur Effect for the ScrollViewer, which will be used to indicate that an install/uninstall is in progress
|
|
||||||
$blurEffect = New-Object Windows.Media.Effects.BlurEffect
|
|
||||||
$blurEffect.Radius = 0
|
|
||||||
$scrollViewer.Effect = $blurEffect
|
|
||||||
|
|
||||||
## Create the ItemsControl, which will be the parent of all the app entries
|
## Create the ItemsControl, which will be the parent of all the app entries
|
||||||
$itemsControl = New-Object Windows.Controls.ItemsControl
|
$itemsControl = New-Object Windows.Controls.ItemsControl
|
||||||
$itemsControl.HorizontalAlignment = 'Stretch'
|
$itemsControl.HorizontalAlignment = 'Stretch'
|
||||||
@@ -52,61 +42,5 @@
|
|||||||
# Add the Border containing the App Area to the target Grid
|
# Add the Border containing the App Area to the target Grid
|
||||||
$targetGrid.Children.Add($Border) | Out-Null
|
$targetGrid.Children.Add($Border) | Out-Null
|
||||||
|
|
||||||
$overlay = New-Object Windows.Controls.Border
|
|
||||||
$overlay.CornerRadius = New-Object Windows.CornerRadius(10)
|
|
||||||
$overlay.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallOverlayBackgroundColor")
|
|
||||||
$overlay.Visibility = [Windows.Visibility]::Collapsed
|
|
||||||
|
|
||||||
# Also add the overlay to the target Grid on top of the App Area
|
|
||||||
$targetGrid.Children.Add($overlay) | Out-Null
|
|
||||||
$sync.InstallAppAreaOverlay = $overlay
|
|
||||||
|
|
||||||
$overlayText = New-Object Windows.Controls.TextBlock
|
|
||||||
$overlayText.Text = "Installing apps..."
|
|
||||||
$overlayText.HorizontalAlignment = 'Center'
|
|
||||||
$overlayText.VerticalAlignment = 'Center'
|
|
||||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "MainForegroundColor")
|
|
||||||
$overlayText.Background = "Transparent"
|
|
||||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontSizeProperty, "HeaderFontSize")
|
|
||||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontFamilyProperty, "MainFontFamily")
|
|
||||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontWeightProperty, "MainFontWeight")
|
|
||||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::MarginProperty, "MainMargin")
|
|
||||||
$sync.InstallAppAreaOverlayText = $overlayText
|
|
||||||
|
|
||||||
$progressbar = New-Object Windows.Controls.ProgressBar
|
|
||||||
$progressbar.Name = "ProgressBar"
|
|
||||||
$progressbar.Width = 250
|
|
||||||
$progressbar.Height = 50
|
|
||||||
$sync.ProgressBar = $progressbar
|
|
||||||
|
|
||||||
# Add a TextBlock overlay for the progress bar text
|
|
||||||
$progressBarTextBlock = New-Object Windows.Controls.TextBlock
|
|
||||||
$progressBarTextBlock.Name = "progressBarTextBlock"
|
|
||||||
$progressBarTextBlock.FontWeight = [Windows.FontWeights]::Bold
|
|
||||||
$progressBarTextBlock.FontSize = 16
|
|
||||||
$progressBarTextBlock.Width = $progressbar.Width
|
|
||||||
$progressBarTextBlock.Height = $progressbar.Height
|
|
||||||
$progressBarTextBlock.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "ProgressBarTextColor")
|
|
||||||
$progressBarTextBlock.TextTrimming = "CharacterEllipsis"
|
|
||||||
$progressBarTextBlock.Background = "Transparent"
|
|
||||||
$sync.progressBarTextBlock = $progressBarTextBlock
|
|
||||||
|
|
||||||
# Create a Grid to overlay the text on the progress bar
|
|
||||||
$progressGrid = New-Object Windows.Controls.Grid
|
|
||||||
$progressGrid.Width = $progressbar.Width
|
|
||||||
$progressGrid.Height = $progressbar.Height
|
|
||||||
$progressGrid.Margin = "0,10,0,10"
|
|
||||||
$progressGrid.Children.Add($progressbar) | Out-Null
|
|
||||||
$progressGrid.Children.Add($progressBarTextBlock) | Out-Null
|
|
||||||
|
|
||||||
$overlayStackPanel = New-Object Windows.Controls.StackPanel
|
|
||||||
$overlayStackPanel.Orientation = "Vertical"
|
|
||||||
$overlayStackPanel.HorizontalAlignment = 'Center'
|
|
||||||
$overlayStackPanel.VerticalAlignment = 'Center'
|
|
||||||
$overlayStackPanel.Children.Add($overlayText) | Out-Null
|
|
||||||
$overlayStackPanel.Children.Add($progressGrid) | Out-Null
|
|
||||||
|
|
||||||
$overlay.Child = $overlayStackPanel
|
|
||||||
|
|
||||||
return $itemsControl
|
return $itemsControl
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function Initialize-WinUtilTabContent {
|
|||||||
"AppX" {
|
"AppX" {
|
||||||
Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2
|
Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2
|
||||||
}
|
}
|
||||||
"Win11 Creator" {
|
"Win11ISO" {
|
||||||
if ($sync.Form -and $sync.Form.Dispatcher) {
|
if ($sync.Form -and $sync.Form.Dispatcher) {
|
||||||
$sync.Form.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null
|
$sync.Form.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -15,32 +15,36 @@ Function Invoke-WinUtilCurrentSystem {
|
|||||||
)
|
)
|
||||||
if ($CheckBox -eq "choco") {
|
if ($CheckBox -eq "choco") {
|
||||||
$apps = (choco list | Select-String -Pattern "^\S+").Matches.Value
|
$apps = (choco list | Select-String -Pattern "^\S+").Matches.Value
|
||||||
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
|
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
|
||||||
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
|
$packageId = ($_.Value.choco -split ";")[-1].Trim()
|
||||||
$dependencies = @($sync.configs.applications.$($psitem.Key).choco -split ";")
|
if ($packageId -ne "na" -and $packageId -in $apps) {
|
||||||
if ($dependencies -in $apps) {
|
Write-Output $_.Key
|
||||||
Write-Output $psitem.name
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($checkbox -eq "winget") {
|
if ($checkbox -eq "winget") {
|
||||||
|
|
||||||
$originalEncoding = [Console]::OutputEncoding
|
$originalEncoding = [Console]::OutputEncoding
|
||||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
try {
|
||||||
$Sync.InstalledPrograms = @("winget", "msstore") | ForEach-Object {
|
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
||||||
winget list -s $psitem | Select-Object -skip 3 | ConvertFrom-String -PropertyNames "Name", "Id", "Version", "Available" -Delimiter '\s{2,}'
|
$installedProgramOutput = @(winget list --accept-source-agreements --disable-interactivity 2>&1)
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "winget list failed with exit code $LASTEXITCODE."
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
[Console]::OutputEncoding = $originalEncoding
|
||||||
}
|
}
|
||||||
[Console]::OutputEncoding = $originalEncoding
|
$installedProgramText = $installedProgramOutput -join "`n"
|
||||||
|
|
||||||
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
|
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
|
||||||
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
|
$packageId = (($_.Value.winget -split ";")[-1] -replace "^msstore:", "").Trim()
|
||||||
$dependencies = @($sync.configs.applications.$($psitem.Key).winget -split ";") | ForEach-Object {
|
if ([string]::IsNullOrWhiteSpace($packageId) -or $packageId -eq "na") {
|
||||||
$psitem -replace "^msstore:", ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dependencies[-1] -in $sync.InstalledPrograms.Id) {
|
$packagePattern = "(?im)[^\S\r\n]{2,}$([regex]::Escape($packageId))(?=[^\S\r\n]{2,}|$)"
|
||||||
Write-Output $psitem.name
|
if ($installedProgramText -match $packagePattern) {
|
||||||
|
Write-Output $_.Key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function Invoke-WinUtilISOMountAndVerify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write-Win11ISOLog "Mounting ISO: $isoPath"
|
Write-Win11ISOLog "Mounting ISO: $isoPath"
|
||||||
Set-WinUtilProgressBar -Label "Mounting ISO..." -Percent 10
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Mounting ISO..." -Percent 10
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Mount-DiskImage -ImagePath $isoPath
|
Mount-DiskImage -ImagePath $isoPath
|
||||||
@@ -59,7 +59,7 @@ function Invoke-WinUtilISOMountAndVerify {
|
|||||||
$driveLetter = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + ":"
|
$driveLetter = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + ":"
|
||||||
Write-Win11ISOLog "Mounted at drive $driveLetter"
|
Write-Win11ISOLog "Mounted at drive $driveLetter"
|
||||||
|
|
||||||
Set-WinUtilProgressBar -Label "Verifying ISO contents..." -Percent 30
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Verifying ISO contents..." -Percent 30
|
||||||
|
|
||||||
$wimPath = Join-Path $driveLetter "sources\install.wim"
|
$wimPath = Join-Path $driveLetter "sources\install.wim"
|
||||||
$esdPath = Join-Path $driveLetter "sources\install.esd"
|
$esdPath = Join-Path $driveLetter "sources\install.esd"
|
||||||
@@ -70,13 +70,13 @@ function Invoke-WinUtilISOMountAndVerify {
|
|||||||
[System.Windows.MessageBox]::Show(
|
[System.Windows.MessageBox]::Show(
|
||||||
"This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.",
|
"This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.",
|
||||||
"Invalid ISO", "OK", "Error")
|
"Invalid ISO", "OK", "Error")
|
||||||
Set-WinUtilProgressBar -Label "" -Percent 0
|
Set-WinUtilTweaksProgressIndicator -Visible $false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath }
|
$activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath }
|
||||||
|
|
||||||
Set-WinUtilProgressBar -Label "Reading image metadata..." -Percent 55
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Reading image metadata..." -Percent 55
|
||||||
$imageInfo = Get-WindowsImage -ImagePath $activeWim | Select-Object ImageIndex, ImageName
|
$imageInfo = Get-WindowsImage -ImagePath $activeWim | Select-Object ImageIndex, ImageName
|
||||||
|
|
||||||
if (-not ($imageInfo | Where-Object { $_.ImageName -match "Windows 11" })) {
|
if (-not ($imageInfo | Where-Object { $_.ImageName -match "Windows 11" })) {
|
||||||
@@ -85,7 +85,7 @@ function Invoke-WinUtilISOMountAndVerify {
|
|||||||
[System.Windows.MessageBox]::Show(
|
[System.Windows.MessageBox]::Show(
|
||||||
"No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.",
|
"No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.",
|
||||||
"Not a Windows 11 ISO", "OK", "Error")
|
"Not a Windows 11 ISO", "OK", "Error")
|
||||||
Set-WinUtilProgressBar -Label "" -Percent 0
|
Set-WinUtilTweaksProgressIndicator -Visible $false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ function Invoke-WinUtilISOMountAndVerify {
|
|||||||
$sync["Win11ISOImagePath"] = $isoPath
|
$sync["Win11ISOImagePath"] = $isoPath
|
||||||
$sync["WPFWin11ISOModifySection"].Visibility = "Visible"
|
$sync["WPFWin11ISOModifySection"].Visibility = "Visible"
|
||||||
|
|
||||||
Set-WinUtilProgressBar -Label "ISO verified" -Percent 100
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "ISO verified" -Percent 100
|
||||||
Write-Win11ISOLog "ISO verified OK. Editions found: $($imageInfo.Count)"
|
Write-Win11ISOLog "ISO verified OK. Editions found: $($imageInfo.Count)"
|
||||||
} catch {
|
} catch {
|
||||||
Write-Win11ISOLog "ERROR during mount/verify: $_"
|
Write-Win11ISOLog "ERROR during mount/verify: $_"
|
||||||
@@ -123,7 +123,7 @@ function Invoke-WinUtilISOMountAndVerify {
|
|||||||
"Error", "OK", "Error")
|
"Error", "OK", "Error")
|
||||||
} finally {
|
} finally {
|
||||||
Start-Sleep -Milliseconds 800
|
Start-Sleep -Milliseconds 800
|
||||||
Set-WinUtilProgressBar -Label "" -Percent 0
|
Set-WinUtilTweaksProgressIndicator -Visible $false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +151,7 @@ function Invoke-WinUtilISOModify {
|
|||||||
|
|
||||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
||||||
$sync["Win11ISOModifying"] = $true
|
$sync["Win11ISOModifying"] = $true
|
||||||
|
$sync["Win11ISOProcessRunning"] = $true
|
||||||
|
|
||||||
$workDir = Join-Path $env:TEMP "WinUtil_Win11ISO_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
$workDir = Join-Path $env:TEMP "WinUtil_Win11ISO_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||||||
if (Test-Path $workDir) {
|
if (Test-Path $workDir) {
|
||||||
@@ -203,9 +204,10 @@ function Invoke-WinUtilISOModify {
|
|||||||
|
|
||||||
function SetProgress($label, $pct) {
|
function SetProgress($label, $pct) {
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = $label
|
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||||
$sync.progressBarTextBlock.ToolTip = $label
|
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,10 +454,12 @@ function Invoke-WinUtilISOModify {
|
|||||||
} finally {
|
} finally {
|
||||||
Start-Sleep -Milliseconds 800
|
Start-Sleep -Milliseconds 800
|
||||||
$sync["Win11ISOModifying"] = $false
|
$sync["Win11ISOModifying"] = $false
|
||||||
|
$sync["Win11ISOProcessRunning"] = $false
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = ""
|
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||||
$sync.progressBarTextBlock.ToolTip = ""
|
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||||
$sync.ProgressBar.Value = 0
|
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = 0
|
||||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
||||||
if ($sync["WPFWin11ISOOutputSection"].Visibility -ne "Visible") {
|
if ($sync["WPFWin11ISOOutputSection"].Visibility -ne "Visible") {
|
||||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Visible"
|
$sync["WPFWin11ISOSelectSection"].Visibility = "Visible"
|
||||||
@@ -514,6 +518,7 @@ function Invoke-WinUtilISOCleanAndReset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $false
|
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $false
|
||||||
|
$sync["Win11ISOProcessRunning"] = $true
|
||||||
|
|
||||||
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||||
$runspace.ApartmentState = "STA"
|
$runspace.ApartmentState = "STA"
|
||||||
@@ -538,9 +543,10 @@ function Invoke-WinUtilISOCleanAndReset {
|
|||||||
|
|
||||||
function SetProgress($label, $pct) {
|
function SetProgress($label, $pct) {
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = $label
|
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||||
$sync.progressBarTextBlock.ToolTip = $label
|
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,20 +634,24 @@ function Invoke-WinUtilISOCleanAndReset {
|
|||||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
||||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
||||||
|
|
||||||
$sync.progressBarTextBlock.Text = ""
|
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||||
$sync.progressBarTextBlock.ToolTip = ""
|
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||||
$sync.ProgressBar.Value = 0
|
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = 0
|
||||||
|
|
||||||
$sync["WPFWin11ISOStatusLog"].Text = "Ready. Please select a Windows 11 ISO to begin."
|
$sync["WPFWin11ISOStatusLog"].Text = "Ready. Please select a Windows 11 ISO to begin."
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
Log "ERROR during Clean & Reset: $_"
|
Log "ERROR during Clean & Reset: $_"
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = ""
|
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||||
$sync.progressBarTextBlock.ToolTip = ""
|
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||||
$sync.ProgressBar.Value = 0
|
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = 0
|
||||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
||||||
})
|
})
|
||||||
|
} finally {
|
||||||
|
$sync["Win11ISOProcessRunning"] = $false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -706,6 +716,7 @@ function Invoke-WinUtilISOExport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $false
|
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $false
|
||||||
|
$sync["Win11ISOProcessRunning"] = $true
|
||||||
|
|
||||||
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||||
$runspace.ApartmentState = "STA"
|
$runspace.ApartmentState = "STA"
|
||||||
@@ -726,9 +737,10 @@ function Invoke-WinUtilISOExport {
|
|||||||
|
|
||||||
function SetProgress($label, $pct) {
|
function SetProgress($label, $pct) {
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = $label
|
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||||
$sync.progressBarTextBlock.ToolTip = $label
|
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,10 +800,12 @@ function Invoke-WinUtilISOExport {
|
|||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
Start-Sleep -Milliseconds 800
|
Start-Sleep -Milliseconds 800
|
||||||
|
$sync["Win11ISOProcessRunning"] = $false
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = ""
|
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||||
$sync.progressBarTextBlock.ToolTip = ""
|
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||||
$sync.ProgressBar.Value = 0
|
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = 0
|
||||||
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true
|
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ function Invoke-WinUtilISOWriteUSB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false
|
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false
|
||||||
|
$sync["Win11ISOProcessRunning"] = $true
|
||||||
Write-Win11ISOLog "Starting USB write to Disk $diskNum..."
|
Write-Win11ISOLog "Starting USB write to Disk $diskNum..."
|
||||||
|
|
||||||
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||||
@@ -86,9 +87,10 @@ function Invoke-WinUtilISOWriteUSB {
|
|||||||
|
|
||||||
function SetProgress($label, $pct) {
|
function SetProgress($label, $pct) {
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = $label
|
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||||
$sync.progressBarTextBlock.ToolTip = $label
|
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,10 +257,12 @@ function Invoke-WinUtilISOWriteUSB {
|
|||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
Start-Sleep -Milliseconds 800
|
Start-Sleep -Milliseconds 800
|
||||||
|
$sync["Win11ISOProcessRunning"] = $false
|
||||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||||
$sync.progressBarTextBlock.Text = ""
|
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||||
$sync.progressBarTextBlock.ToolTip = ""
|
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||||
$sync.ProgressBar.Value = 0
|
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||||
|
$sync["WPFTweaksProgressValue"].Value = 0
|
||||||
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
|
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,9 @@ function Remove-WinUtilProvisionedAPPX {
|
|||||||
$removalOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $PackageList 2>&1
|
$removalOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $PackageList 2>&1
|
||||||
if ($LASTEXITCODE -ne 0 -or $null -ne $removalOutput) {
|
if ($LASTEXITCODE -ne 0 -or $null -ne $removalOutput) {
|
||||||
$failureDetails = ($removalOutput | Out-String).Trim()
|
$failureDetails = ($removalOutput | Out-String).Trim()
|
||||||
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "AppX provisioned package removal failed: $failureDetails"
|
$errorMessage = "AppX provisioned package removal failed: $failureDetails"
|
||||||
return
|
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message $errorMessage
|
||||||
|
throw $errorMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-WinUtilLog -Component "AppX" -Message "AppX provisioned package removal completed."
|
Write-WinUtilLog -Component "AppX" -Message "AppX provisioned package removal completed."
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
function Save-WinUtilFile {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Downloads a file and reports transfer progress.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[uri]$Uri,
|
||||||
|
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$DestinationPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[scriptblock]$ProgressCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
$response = $null
|
||||||
|
$responseStream = $null
|
||||||
|
$outputStream = $null
|
||||||
|
|
||||||
|
try {
|
||||||
|
$request = [System.Net.WebRequest]::Create($Uri)
|
||||||
|
$response = $request.GetResponse()
|
||||||
|
$totalBytes = $response.ContentLength
|
||||||
|
$responseStream = $response.GetResponseStream()
|
||||||
|
$outputStream = [System.IO.File]::Create($DestinationPath)
|
||||||
|
$buffer = New-Object byte[] 81920
|
||||||
|
$downloadedBytes = 0L
|
||||||
|
$lastPercent = -1
|
||||||
|
|
||||||
|
while (($bytesRead = $responseStream.Read($buffer, 0, $buffer.Length)) -gt 0) {
|
||||||
|
$outputStream.Write($buffer, 0, $bytesRead)
|
||||||
|
$downloadedBytes += $bytesRead
|
||||||
|
|
||||||
|
if ($totalBytes -gt 0) {
|
||||||
|
$percent = [Math]::Min(100, [int](($downloadedBytes / $totalBytes) * 100))
|
||||||
|
if ($percent -ne $lastPercent) {
|
||||||
|
& $ProgressCallback $percent
|
||||||
|
$lastPercent = $percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lastPercent -ne 100) {
|
||||||
|
& $ProgressCallback 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($null -ne $outputStream) {
|
||||||
|
$outputStream.Dispose()
|
||||||
|
}
|
||||||
|
if ($null -ne $responseStream) {
|
||||||
|
$responseStream.Dispose()
|
||||||
|
}
|
||||||
|
if ($null -ne $response) {
|
||||||
|
$response.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
function Set-WinUtilProgressbar{
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
This function is used to Update the Progress Bar displayed in the winutil GUI.
|
|
||||||
It will be automatically hidden if the user clicks something and no process is running
|
|
||||||
.PARAMETER Label
|
|
||||||
The Text to be overlaid onto the Progress Bar
|
|
||||||
.PARAMETER PERCENT
|
|
||||||
The percentage of the Progress Bar that should be filled (0-100)
|
|
||||||
#>
|
|
||||||
param(
|
|
||||||
[string]$Label,
|
|
||||||
[ValidateRange(0,100)]
|
|
||||||
[int]$Percent
|
|
||||||
)
|
|
||||||
|
|
||||||
$progressLabel = $Label
|
|
||||||
|
|
||||||
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.Text = $progressLabel}
|
|
||||||
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.ToolTip = $progressLabel}
|
|
||||||
if ($Percent -lt 5 ) {
|
|
||||||
$Percent = 5 # Ensure the progress bar is not empty, as it looks weird
|
|
||||||
}
|
|
||||||
Invoke-WPFUIThread -ScriptBlock { $sync.ProgressBar.Value = $Percent}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
function Set-WinUtilTweaksProgressIndicator {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Shows, updates, or hides the window-level progress indicator used by long-running
|
||||||
|
workflows such as app management, Tweaks, AppX management, and Win11 Creator.
|
||||||
|
It lives outside the TabControl, so 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
function Show-WPFInstallAppBusy {
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Displays a busy overlay in the install app area of the WPF form.
|
|
||||||
This is used to indicate that an install or uninstall is in progress.
|
|
||||||
Dynamically updates the size of the overlay based on the app area on each invocation.
|
|
||||||
.PARAMETER text
|
|
||||||
The text to display in the busy overlay. Defaults to "Installing apps...".
|
|
||||||
#>
|
|
||||||
param (
|
|
||||||
$text = "Installing apps..."
|
|
||||||
)
|
|
||||||
$overlayText = $text
|
|
||||||
|
|
||||||
Invoke-WPFUIThread -ScriptBlock {
|
|
||||||
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Visible
|
|
||||||
$sync.InstallAppAreaOverlay.Width = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4)
|
|
||||||
$sync.InstallAppAreaOverlay.Height = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4)
|
|
||||||
$sync.InstallAppAreaOverlayText.Text = $overlayText
|
|
||||||
$sync.InstallAppAreaBorder.IsEnabled = $false
|
|
||||||
$sync.InstallAppAreaScrollViewer.Effect.Radius = 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,57 +1,100 @@
|
|||||||
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()
|
$packageList = [System.Collections.Generic.List[string]]::new()
|
||||||
|
|
||||||
foreach ($key in $selected) {
|
try {
|
||||||
if ($key -eq "WPFAppxMicrosoft_XboxGamingOverlay") {
|
Write-WinUtilLog -Component "AppX" -Message "Starting AppX removal for $totalPackages selected package(s)."
|
||||||
# Making sure Game Bar isn't running
|
if ($hasUI) {
|
||||||
Write-WinUtilLog -Component "AppX" -Message "Stopping GameBarFTServer before removing Xbox Gaming Overlay."
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing AppX removal (0/$totalPackages)" -Percent 0
|
||||||
Stop-Process -Name GameBarFTServer -Force -Confirm:$false -ErrorAction SilentlyContinue
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
|
||||||
|
|
||||||
# This stops annoying ms-gamebar popup when launching games.
|
|
||||||
Write-WinUtilLog -Component "AppX" -Message "Disabling Game DVR capture before removing Xbox Gaming Overlay."
|
|
||||||
Set-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR -Name AppCaptureEnabled -Value 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($key -eq "WPFAppxMicrosoft_WindowsNotepad") {
|
for ($index = 0; $index -lt $totalPackages; $index++) {
|
||||||
Write-WinUtilLog -Component "AppX" -Message "Stopping dllhost before removing Notepad."
|
$key = $selected[$index]
|
||||||
Stop-Process -Name dllhost -Force -Confirm:$false -ErrorAction SilentlyContinue
|
$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
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key -eq "WPFAppxMicrosoft_XboxGamingOverlay") {
|
||||||
|
# Making sure Game Bar isn't running
|
||||||
|
Write-WinUtilLog -Component "AppX" -Message "Stopping GameBarFTServer before removing Xbox Gaming Overlay."
|
||||||
|
Stop-Process -Name GameBarFTServer -Force -Confirm:$false -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# This stops annoying ms-gamebar popup when launching games.
|
||||||
|
Write-WinUtilLog -Component "AppX" -Message "Disabling Game DVR capture before removing Xbox Gaming Overlay."
|
||||||
|
Set-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR -Name AppCaptureEnabled -Value 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key -eq "WPFAppxMicrosoft_WindowsNotepad") {
|
||||||
|
Write-WinUtilLog -Component "AppX" -Message "Stopping dllhost before removing Notepad."
|
||||||
|
Stop-Process -Name dllhost -Force -Confirm:$false -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Removing $($app.Content)"
|
||||||
|
Write-WinUtilLog -Component "AppX" -Message "Removing $($app.Content) ($($app.PackageId))."
|
||||||
|
Remove-WinUtilAPPX -Name $app.PackageId
|
||||||
|
$packageList.Add($app.PackageId)
|
||||||
|
|
||||||
|
if ($key -eq "WPFAppxMSTeams") {
|
||||||
|
# Uninstalls Microsoft Teams Meeting Add-in for Microsoft Office
|
||||||
|
Write-WinUtilLog -Component "AppX" -Message "Uninstalling Microsoft Teams meeting add-in package."
|
||||||
|
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) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Removing $($apps[$key].Content)"
|
if ($packageList.Count -gt 0) {
|
||||||
Write-WinUtilLog -Component "AppX" -Message "Removing $($apps[$key].Content) ($($apps[$key].PackageId))."
|
if ($hasUI) {
|
||||||
Remove-WinUtilAPPX -Name $apps[$key].PackageId
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Removing provisioned AppX packages" -Percent 90
|
||||||
$packageList.Add($apps[$key].PackageId)
|
}
|
||||||
|
Remove-WinUtilProvisionedAPPX -PackageList $packageList.ToArray()
|
||||||
|
}
|
||||||
|
|
||||||
if ($key -eq "WPFAppxMSTeams") {
|
Write-Host "================================="
|
||||||
# Uninstalls Microsoft Teams Meeting Add-in for Microsoft Office
|
Write-Host "-- AppX Removal Finished ---"
|
||||||
Write-WinUtilLog -Component "AppX" -Message "Uninstalling Microsoft Teams meeting add-in package."
|
Write-Host "================================="
|
||||||
Get-Package -Name "Microsoft Teams*" -ErrorAction SilentlyContinue | Uninstall-Package -Force
|
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 {
|
||||||
if ($packageList.Count -gt 0) {
|
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "AppX removal failed: $($_.Exception.Message)"
|
||||||
Remove-WinUtilProvisionedAPPX -PackageList $packageList.ToArray()
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "================================="
|
|
||||||
Write-Host "-- AppX Removal Finished ---"
|
|
||||||
Write-Host "================================="
|
|
||||||
Write-WinUtilLog -Component "AppX" -Message "AppX removal finished."
|
|
||||||
|
|
||||||
$sync.ProcessRunning = $false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ function Invoke-WPFButton {
|
|||||||
|
|
||||||
# Use this to get the name of the button
|
# Use this to get the name of the button
|
||||||
#[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 -and -not $sync.Win11ISOProcessRunning) {
|
||||||
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
|
||||||
@@ -67,6 +67,7 @@ function Invoke-WPFButton {
|
|||||||
"WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"}
|
"WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"}
|
||||||
"WPFAppxRemoval" {Invoke-WPFTab "WPFTab6BT"}
|
"WPFAppxRemoval" {Invoke-WPFTab "WPFTab6BT"}
|
||||||
"WPFBackToTweaks" {Invoke-WPFTab "WPFTab2BT"}
|
"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" {
|
||||||
@@ -76,7 +77,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
|
||||||
@@ -85,6 +86,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,6 +1,5 @@
|
|||||||
function Invoke-WPFGetInstalled {
|
function Invoke-WPFGetInstalled {
|
||||||
<#
|
<#
|
||||||
TODO: Add the Option to use Chocolatey as Engine
|
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Invokes the function that gets the checkboxes to check in a new runspace
|
Invokes the function that gets the checkboxes to check in a new runspace
|
||||||
|
|
||||||
@@ -19,35 +18,72 @@ function Invoke-WPFGetInstalled {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
$managerPreference = $sync.preferences.packagemanager
|
$managerPreference = $sync.preferences.packagemanager
|
||||||
|
$operation = [Hashtable]::Synchronized(@{
|
||||||
Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -ScriptBlock {
|
Checkboxes = @()
|
||||||
param (
|
Error = $null
|
||||||
[string]$checkbox,
|
})
|
||||||
[string]$managerPreference
|
$completeAction = [Action[hashtable, string]]{
|
||||||
|
param(
|
||||||
|
[hashtable]$completedOperation,
|
||||||
|
[string]$completedCheckbox
|
||||||
)
|
)
|
||||||
$sync.ProcessRunning = $true
|
try {
|
||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" }
|
if ($completedOperation.Error) {
|
||||||
|
Write-WinUtilLog -Level "ERROR" -Component "Install" -Message "Get installed state failed: $($completedOperation.Error)"
|
||||||
|
Write-Warning "Unable to get installed state: $($completedOperation.Error)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if ($checkbox -eq "winget") {
|
if ($completedCheckbox -eq "winget") {
|
||||||
Write-Host "Getting Installed Programs..."
|
foreach ($checkboxName in $completedOperation.Checkboxes) {
|
||||||
switch ($managerPreference) {
|
if (-not $sync.selectedApps.Contains($checkboxName)) {
|
||||||
"Choco"{$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox "choco"; break}
|
$sync.selectedApps.Add($checkboxName)
|
||||||
"Winget"{$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox $checkbox; break}
|
}
|
||||||
|
}
|
||||||
|
Reset-WPFCheckBoxes -checkboxfilterpattern "WPFInstall*"
|
||||||
|
} else {
|
||||||
|
foreach ($checkboxName in $completedOperation.Checkboxes) {
|
||||||
|
$sync.$checkboxName.ischecked = $True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$sync.ProcessRunning = $false
|
||||||
|
Set-WinUtilTaskbaritem -state "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sync.ProcessRunning = $true
|
||||||
|
Set-WinUtilTaskbaritem -state "Indeterminate"
|
||||||
|
try {
|
||||||
|
Invoke-WPFRunspace -ParameterList @(
|
||||||
|
("managerPreference", $managerPreference),
|
||||||
|
("checkbox", $checkbox),
|
||||||
|
("operation", $operation),
|
||||||
|
("completeAction", $completeAction)
|
||||||
|
) -ScriptBlock {
|
||||||
|
param (
|
||||||
|
[string]$checkbox,
|
||||||
|
[string]$managerPreference,
|
||||||
|
[hashtable]$operation,
|
||||||
|
[Action[hashtable, string]]$completeAction
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if ($checkbox -eq "winget") {
|
||||||
|
switch ($managerPreference) {
|
||||||
|
"Choco" { $operation.Checkboxes = @(Invoke-WinUtilCurrentSystem -CheckBox "choco"); break }
|
||||||
|
"Winget" { $operation.Checkboxes = @(Invoke-WinUtilCurrentSystem -CheckBox $checkbox); break }
|
||||||
|
}
|
||||||
|
} elseif ($checkbox -eq "tweaks") {
|
||||||
|
$operation.Checkboxes = @(Invoke-WinUtilCurrentSystem -CheckBox $checkbox)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
$operation.Error = $_.Exception.Message
|
||||||
|
} finally {
|
||||||
|
$sync.Form.Dispatcher.BeginInvoke($completeAction, [object[]]@($operation, $checkbox)) | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ($checkbox -eq "tweaks") {
|
} catch {
|
||||||
Write-Host "Getting Installed Tweaks..."
|
$operation.Error = $_.Exception.Message
|
||||||
$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox $checkbox
|
$completeAction.Invoke($operation, $checkbox)
|
||||||
}
|
|
||||||
|
|
||||||
$sync.form.Dispatcher.invoke({
|
|
||||||
foreach ($checkbox in $Checkboxes) {
|
|
||||||
$sync.$checkbox.ischecked = $True
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Write-Host "Done..."
|
|
||||||
$sync.ProcessRunning = $false
|
|
||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,33 +31,81 @@ function Invoke-WPFInstall {
|
|||||||
|
|
||||||
$packagesWinget = $packagesSorted['Winget']
|
$packagesWinget = $packagesSorted['Winget']
|
||||||
$packagesChoco = $packagesSorted['Choco']
|
$packagesChoco = $packagesSorted['Choco']
|
||||||
|
$totalPackages = @($packagesWinget).Count + @($packagesChoco).Count
|
||||||
|
$completedPackages = 0
|
||||||
|
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
|
||||||
Write-WinUtilLog -Component "Install" -Message "Install package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)"
|
Write-WinUtilLog -Component "Install" -Message "Install package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$sync.ProcessRunning = $true
|
$sync.ProcessRunning = $true
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing app install (0/$totalPackages)" -Percent 0
|
||||||
|
Invoke-WPFUIThread -ScriptBlock {
|
||||||
|
if ($null -ne $sync.ItemsControl) {
|
||||||
|
$sync.ItemsControl.IsEnabled = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($packagesWinget.Count -gt 0 -and $packagesWinget -ne "0") {
|
if($packagesWinget.Count -gt 0 -and $packagesWinget -ne "0") {
|
||||||
Show-WPFInstallAppBusy -text "Installing apps..."
|
|
||||||
Install-WinUtilWinget
|
Install-WinUtilWinget
|
||||||
Install-WinUtilProgramWinget -Action Install -Programs $packagesWinget
|
foreach ($program in $packagesWinget) {
|
||||||
|
$position = $completedPackages + 1
|
||||||
|
$startPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installing $program ($position/$totalPackages)" -Percent $startPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-WinUtilProgramWinget -Action Install -Programs @($program)
|
||||||
|
$completedPackages++
|
||||||
|
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installed $program ($completedPackages/$totalPackages)" -Percent $completedPercent
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if($packagesChoco.Count -gt 0) {
|
if($packagesChoco.Count -gt 0) {
|
||||||
|
$position = $completedPackages + 1
|
||||||
|
$startPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installing Chocolatey packages ($position/$totalPackages)" -Percent $startPercent
|
||||||
|
}
|
||||||
|
|
||||||
Install-WinUtilChoco
|
Install-WinUtilChoco
|
||||||
Install-WinUtilProgramChoco -Action Install -Programs $packagesChoco
|
Install-WinUtilProgramChoco -Action Install -Programs $packagesChoco
|
||||||
|
$completedPackages += @($packagesChoco).Count
|
||||||
|
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Installed Chocolatey packages ($completedPackages/$totalPackages)" -Percent $completedPercent
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Hide-WPFInstallAppBusy
|
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-Host "-- Installs have finished ---"
|
Write-Host "-- Installs have finished ---"
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-WinUtilLog -Component "Install" -Message "Install workflow completed."
|
Write-WinUtilLog -Component "Install" -Message "Install workflow completed."
|
||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App install finished" -Percent 100
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Hide-WPFInstallAppBusy
|
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-Host "Error: $_"
|
Write-Host "Error: $_"
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-WinUtilLog -Level "ERROR" -Component "Install" -Message "Install workflow failed: $($_.Exception.Message)"
|
Write-WinUtilLog -Level "ERROR" -Component "Install" -Message "Install workflow failed: $($_.Exception.Message)"
|
||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App install failed" -Percent 100
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
if ($hasUI) {
|
||||||
|
Invoke-WPFUIThread -ScriptBlock {
|
||||||
|
if ($null -ne $sync.ItemsControl) {
|
||||||
|
$sync.ItemsControl.IsEnabled = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
$sync.ProcessRunning = $False
|
$sync.ProcessRunning = $False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,50 @@
|
|||||||
function Invoke-WPFOOSU {
|
function Invoke-WPFOOSU {
|
||||||
try {
|
if ($sync.ProcessRunning) {
|
||||||
$ProgressPreference = 'SilentlyContinue'
|
Show-WinUtilMessage -Message "Another process is currently running." -Title "WinUtil" -Button "OK" -Icon "Warning"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Invoke-WebRequest -Uri https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe -OutFile "$winutildir\ooshutup10.exe"
|
$downloadPath = Join-Path $sync.winutildir "ooshutup10.exe"
|
||||||
Start-Process -FilePath "$winutildir\ooshutup10.exe"
|
$sync.ProcessRunning = $true
|
||||||
|
|
||||||
$ProgressPreference = 'Continue'
|
Invoke-WPFRunspace -ParameterList @(,("downloadPath", $downloadPath)) -ScriptBlock {
|
||||||
} catch {
|
param($downloadPath)
|
||||||
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
|
||||||
|
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
|
||||||
|
|
||||||
|
try {
|
||||||
|
Write-WinUtilLog -Component "OOSU" -Message "Downloading O&O ShutUp10++."
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ (0%)" -Percent 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Save-WinUtilFile -Uri "https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe" -DestinationPath $downloadPath -ProgressCallback {
|
||||||
|
param($percent)
|
||||||
|
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ ($percent%)" -Percent $percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Launching O&O ShutUp10++" -Percent 100
|
||||||
|
}
|
||||||
|
Start-Process -FilePath $downloadPath
|
||||||
|
|
||||||
|
Write-WinUtilLog -Component "OOSU" -Message "O&O ShutUp10++ launched."
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ launched" -Percent 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-WinUtilLog -Level "ERROR" -Component "OOSU" -Message "O&O ShutUp10++ download failed: $($_.Exception.Message)"
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ download failed" -Percent 100
|
||||||
|
}
|
||||||
|
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
$sync.ProcessRunning = $false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ public sealed class WinUtilRunspaceCleanupState
|
|||||||
|
|
||||||
public static class WinUtilRunspaceCleanup
|
public static class WinUtilRunspaceCleanup
|
||||||
{
|
{
|
||||||
|
public static readonly System.Threading.WaitOrTimerCallback Callback = Cleanup;
|
||||||
|
|
||||||
public static void Cleanup(object state, bool timedOut)
|
public static void Cleanup(object state, bool timedOut)
|
||||||
{
|
{
|
||||||
var cleanupState = state as WinUtilRunspaceCleanupState;
|
var cleanupState = state as WinUtilRunspaceCleanupState;
|
||||||
@@ -89,8 +91,7 @@ public static class WinUtilRunspaceCleanup
|
|||||||
$cleanupState = [WinUtilRunspaceCleanupState]::new()
|
$cleanupState = [WinUtilRunspaceCleanupState]::new()
|
||||||
$cleanupState.PowerShell = $powershell
|
$cleanupState.PowerShell = $powershell
|
||||||
$cleanupState.Handle = $handle
|
$cleanupState.Handle = $handle
|
||||||
$cleanupCallback = [System.Threading.WaitOrTimerCallback][WinUtilRunspaceCleanup]::Cleanup
|
[System.Threading.ThreadPool]::RegisterWaitForSingleObject($handle.AsyncWaitHandle, [WinUtilRunspaceCleanup]::Callback, $cleanupState, -1, $true) | Out-Null
|
||||||
[System.Threading.ThreadPool]::RegisterWaitForSingleObject($handle.AsyncWaitHandle, $cleanupCallback, $cleanupState, -1, $true) | Out-Null
|
|
||||||
|
|
||||||
# Return the handle
|
# Return the handle
|
||||||
return $handle
|
return $handle
|
||||||
|
|||||||
@@ -7,11 +7,21 @@ function Invoke-WPFSelectedCheckboxesUpdate ($type, $checkboxName) {
|
|||||||
'^WPFAppx' { 'selectedAppx' }
|
'^WPFAppx' { 'selectedAppx' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$selectionChanged = $false
|
||||||
if ($type -eq "Add") {
|
if ($type -eq "Add") {
|
||||||
if (-not $sync.$listName.Contains($checkboxName)) {
|
if (-not $sync.$listName.Contains($checkboxName)) {
|
||||||
$sync.$listName.Add($checkboxName)
|
$sync.$listName.Add($checkboxName)
|
||||||
|
$selectionChanged = $true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$sync.$listName.Remove($checkboxName)
|
$selectionChanged = $sync.$listName.Remove($checkboxName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($listName -eq "selectedApps" -and $selectionChanged) {
|
||||||
|
$sync.WPFselectedAppsButton.Content = "Selected Apps: $($sync.selectedApps.Count)"
|
||||||
|
$sync.selectedAppsstackPanel.Children.Clear()
|
||||||
|
$sync.selectedApps | Sort-Object | ForEach-Object {
|
||||||
|
Add-SelectedAppsMenuItem -name $sync.configs.applicationsHashtable.$_.Content -key $_
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,6 +409,11 @@ function Invoke-WPFUIElements {
|
|||||||
$textBlock.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "FontSize")
|
$textBlock.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "FontSize")
|
||||||
$textBlock.Tag = $checkBox
|
$textBlock.Tag = $checkBox
|
||||||
|
|
||||||
|
$textBlock.Add_MouseUp({
|
||||||
|
[System.Object]$Sender = $args[0]
|
||||||
|
Start-Process $Sender.ToolTip -ErrorAction Stop
|
||||||
|
})
|
||||||
|
|
||||||
$updateLinkMargin = {
|
$updateLinkMargin = {
|
||||||
[System.Object]$Sender = $args[0]
|
[System.Object]$Sender = $args[0]
|
||||||
$linkedCheckBox = $Sender.Tag
|
$linkedCheckBox = $Sender.Tag
|
||||||
|
|||||||
@@ -42,11 +42,21 @@ function Invoke-WPFUnInstall {
|
|||||||
|
|
||||||
$packagesWinget = $packagesSorted['Winget']
|
$packagesWinget = $packagesSorted['Winget']
|
||||||
$packagesChoco = $packagesSorted['Choco']
|
$packagesChoco = $packagesSorted['Choco']
|
||||||
|
$totalPackages = @($packagesWinget).Count + @($packagesChoco).Count
|
||||||
|
$completedPackages = 0
|
||||||
|
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
|
||||||
Write-WinUtilLog -Component "Uninstall" -Message "Uninstall package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)"
|
Write-WinUtilLog -Component "Uninstall" -Message "Uninstall package manager split: winget=$(@($packagesWinget).Count), choco=$(@($packagesChoco).Count)"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$sync.ProcessRunning = $true
|
$sync.ProcessRunning = $true
|
||||||
Show-WPFInstallAppBusy -text "Uninstalling apps..."
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Preparing app uninstall (0/$totalPackages)" -Percent 0
|
||||||
|
Invoke-WPFUIThread -ScriptBlock {
|
||||||
|
if ($null -ne $sync.ItemsControl) {
|
||||||
|
$sync.ItemsControl.IsEnabled = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($packagesWinget -contains "Microsoft.Edge") {
|
if ($packagesWinget -contains "Microsoft.Edge") {
|
||||||
New-Item -Path "$Env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\MicrosoftEdge.exe" -Force
|
New-Item -Path "$Env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\MicrosoftEdge.exe" -Force
|
||||||
@@ -54,25 +64,62 @@ function Invoke-WPFUnInstall {
|
|||||||
|
|
||||||
# Uninstall all selected programs in new window
|
# Uninstall all selected programs in new window
|
||||||
if($packagesWinget.Count -gt 0) {
|
if($packagesWinget.Count -gt 0) {
|
||||||
Install-WinUtilProgramWinget -Action Uninstall -Programs $packagesWinget
|
foreach ($program in $packagesWinget) {
|
||||||
|
$position = $completedPackages + 1
|
||||||
|
$startPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalling $program ($position/$totalPackages)" -Percent $startPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-WinUtilProgramWinget -Action Uninstall -Programs @($program)
|
||||||
|
$completedPackages++
|
||||||
|
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalled $program ($completedPackages/$totalPackages)" -Percent $completedPercent
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if($packagesChoco.Count -gt 0) {
|
if($packagesChoco.Count -gt 0) {
|
||||||
|
$position = $completedPackages + 1
|
||||||
|
$startPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalling Chocolatey packages ($position/$totalPackages)" -Percent $startPercent
|
||||||
|
}
|
||||||
|
|
||||||
Install-WinUtilProgramChoco -Action Uninstall -Programs $packagesChoco
|
Install-WinUtilProgramChoco -Action Uninstall -Programs $packagesChoco
|
||||||
|
$completedPackages += @($packagesChoco).Count
|
||||||
|
$completedPercent = [int](($completedPackages / $totalPackages) * 100)
|
||||||
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Uninstalled Chocolatey packages ($completedPackages/$totalPackages)" -Percent $completedPercent
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($completedPercent / 100) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Hide-WPFInstallAppBusy
|
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-Host "-- Uninstalls have finished ---"
|
Write-Host "-- Uninstalls have finished ---"
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-WinUtilLog -Component "Uninstall" -Message "Uninstall workflow completed."
|
Write-WinUtilLog -Component "Uninstall" -Message "Uninstall workflow completed."
|
||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App uninstall finished" -Percent 100
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Hide-WPFInstallAppBusy
|
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-Host "Error: $_"
|
Write-Host "Error: $_"
|
||||||
Write-Host "==========================================="
|
Write-Host "==========================================="
|
||||||
Write-WinUtilLog -Level "ERROR" -Component "Uninstall" -Message "Uninstall workflow failed: $($_.Exception.Message)"
|
Write-WinUtilLog -Level "ERROR" -Component "Uninstall" -Message "Uninstall workflow failed: $($_.Exception.Message)"
|
||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
|
if ($hasUI) {
|
||||||
|
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "App uninstall failed" -Percent 100
|
||||||
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
if ($hasUI) {
|
||||||
|
Invoke-WPFUIThread -ScriptBlock {
|
||||||
|
if ($null -ne $sync.ItemsControl) {
|
||||||
|
$sync.ItemsControl.IsEnabled = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
$sync.ProcessRunning = $False
|
$sync.ProcessRunning = $False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,22 +5,51 @@ function Invoke-WPFUpdatesdefault {
|
|||||||
Resets Windows Update settings to default
|
Resets Windows Update settings to default
|
||||||
|
|
||||||
#>
|
#>
|
||||||
$ErrorActionPreference = 'SilentlyContinue'
|
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Resetting Windows Update settings to default."
|
Write-WinUtilLog -Component "Updates" -Message "Resetting Windows Update settings to default."
|
||||||
|
|
||||||
Write-Host "Removing Windows Update policy settings..." -ForegroundColor Green
|
Write-Host "Removing Windows Update settings managed by WinUtil..." -ForegroundColor Green
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Removing Windows Update policy registry paths."
|
Write-WinUtilLog -Component "Updates" -Message "Removing Windows Update registry values managed by WinUtil."
|
||||||
|
|
||||||
Remove-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Recurse -Force
|
$registryValues = @(
|
||||||
Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization" -Recurse -Force
|
@{
|
||||||
Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Recurse -Force
|
Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
|
||||||
Remove-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Recurse -Force
|
Names = @("NoAutoUpdate", "AUOptions", "NoAutoRebootWithLoggedOnUsers", "AUPowerManagement")
|
||||||
Remove-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Recurse -Force
|
},
|
||||||
Remove-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Recurse -Force
|
@{
|
||||||
|
Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
|
||||||
|
Names = @("ExcludeWUDriversInQualityUpdate", "DeferFeatureUpdates", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdates", "DeferQualityUpdatesPeriodInDays")
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Path = "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings"
|
||||||
|
Names = @("BranchReadinessLevel", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdatesPeriodInDays")
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata"
|
||||||
|
Names = @("PreventDeviceMetadataFromNetwork")
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching"
|
||||||
|
Names = @("DontPromptForWindowsUpdate", "DontSearchWindowsUpdate", "DriverUpdateWizardWuSearchEnabled")
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config"
|
||||||
|
Names = @("DODownloadMode")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Write-Host "Showing Windows Updates in settings..."
|
foreach ($registryEntry in $registryValues) {
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Showing Windows Update settings page."
|
foreach ($valueName in $registryEntry.Names) {
|
||||||
Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name SettingsPageVisibility
|
Remove-ItemProperty -Path $registryEntry.Path -Name $valueName -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$explorerPolicyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"
|
||||||
|
$settingsPageVisibility = (Get-ItemProperty -Path $explorerPolicyPath -Name "SettingsPageVisibility" -ErrorAction SilentlyContinue).SettingsPageVisibility
|
||||||
|
if ($settingsPageVisibility -eq "hide:windowsupdate") {
|
||||||
|
Write-Host "Removing WinUtil's legacy Windows Update page restriction..."
|
||||||
|
Write-WinUtilLog -Component "Updates" -Message "Removing the legacy Windows Update settings page restriction."
|
||||||
|
Remove-ItemProperty -Path $explorerPolicyPath -Name "SettingsPageVisibility" -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Reenabling Windows Update Services..." -ForegroundColor Green
|
Write-Host "Reenabling Windows Update Services..." -ForegroundColor Green
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Restoring Windows Update service startup types."
|
Write-WinUtilLog -Component "Updates" -Message "Restoring Windows Update service startup types."
|
||||||
@@ -35,12 +64,8 @@ function Invoke-WPFUpdatesdefault {
|
|||||||
|
|
||||||
Write-Host "Restored UsoSvc to Automatic."
|
Write-Host "Restored UsoSvc to Automatic."
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Starting UsoSvc service and restoring startup type to Automatic."
|
Write-WinUtilLog -Component "Updates" -Message "Starting UsoSvc service and restoring startup type to Automatic."
|
||||||
Start-Service -Name UsoSvc
|
|
||||||
Set-Service -Name UsoSvc -StartupType Automatic
|
Set-Service -Name UsoSvc -StartupType Automatic
|
||||||
|
Start-Service -Name UsoSvc
|
||||||
Write-Host "Restored WaaSMedicSvc to Manual."
|
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Restoring WaaSMedicSvc service to Manual."
|
|
||||||
Set-Service -Name WaaSMedicSvc -StartupType Manual
|
|
||||||
|
|
||||||
Write-Host "Enabling update related scheduled tasks..." -ForegroundColor Green
|
Write-Host "Enabling update related scheduled tasks..." -ForegroundColor Green
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Enabling update related scheduled tasks."
|
Write-WinUtilLog -Component "Updates" -Message "Enabling update related scheduled tasks."
|
||||||
@@ -54,13 +79,9 @@ function Invoke-WPFUpdatesdefault {
|
|||||||
'\Microsoft\WindowsUpdate\*'
|
'\Microsoft\WindowsUpdate\*'
|
||||||
|
|
||||||
foreach ($Task in $Tasks) {
|
foreach ($Task in $Tasks) {
|
||||||
Get-ScheduledTask -TaskPath $Task | Enable-ScheduledTask -ErrorAction SilentlyContinue
|
Get-ScheduledTask -TaskPath $Task -ErrorAction SilentlyContinue | Enable-ScheduledTask -ErrorAction SilentlyContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Windows Local Policies Reset to Default."
|
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Resetting local security policy to defaults with secedit."
|
|
||||||
secedit /configure /cfg "$Env:SystemRoot\inf\defltbase.inf" /db defltbase.sdb
|
|
||||||
|
|
||||||
Write-Host "===================================================" -ForegroundColor Green
|
Write-Host "===================================================" -ForegroundColor Green
|
||||||
Write-Host "--- Windows Update Settings Reset to Default ---" -ForegroundColor Green
|
Write-Host "--- Windows Update Settings Reset to Default ---" -ForegroundColor Green
|
||||||
Write-Host "===================================================" -ForegroundColor Green
|
Write-Host "===================================================" -ForegroundColor Green
|
||||||
|
|||||||
@@ -8,7 +8,17 @@ function Invoke-WPFUpdatesdisable {
|
|||||||
Disabling Windows Update is not recommended. This is only for advanced users who know what they are doing.
|
Disabling Windows Update is not recommended. This is only for advanced users who know what they are doing.
|
||||||
|
|
||||||
#>
|
#>
|
||||||
$ErrorActionPreference = 'SilentlyContinue'
|
$confirmation = Show-WinUtilMessage `
|
||||||
|
-Message "Disabling Windows Update stops update services, disables scheduled tasks, and clears downloaded update files. Security updates will not be installed until defaults are restored. Continue?" `
|
||||||
|
-Title "Disable Windows Update?" `
|
||||||
|
-Button "YesNo" `
|
||||||
|
-Icon "Warning"
|
||||||
|
|
||||||
|
if ($confirmation -ne "Yes") {
|
||||||
|
Write-WinUtilLog -Component "Updates" -Message "Windows Update disable workflow cancelled."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Disabling Windows Update settings."
|
Write-WinUtilLog -Component "Updates" -Message "Disabling Windows Update settings."
|
||||||
|
|
||||||
Write-Host "Configuring registry settings..." -ForegroundColor Yellow
|
Write-Host "Configuring registry settings..." -ForegroundColor Yellow
|
||||||
@@ -21,24 +31,14 @@ function Invoke-WPFUpdatesdisable {
|
|||||||
New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Force
|
New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Force
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Name "DODownloadMode" -Type DWord -Value 0
|
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Name "DODownloadMode" -Type DWord -Value 0
|
||||||
|
|
||||||
Write-Host "Hiding Windows Updates from settings..."
|
foreach ($serviceName in @("BITS", "wuauserv", "UsoSvc")) {
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Hiding Windows Update settings page."
|
Write-Host "Stopping and disabling $serviceName service."
|
||||||
Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name SettingsPageVisibility -Value hide:windowsupdate
|
Write-WinUtilLog -Component "Updates" -Message "Stopping and disabling $serviceName service."
|
||||||
|
Stop-Service -Name $serviceName -Force -ErrorAction SilentlyContinue
|
||||||
|
Set-Service -Name $serviceName -StartupType Disabled
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Disabled BITS Service."
|
Remove-Item -Path "C:\Windows\SoftwareDistribution\*" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Disabling BITS service."
|
|
||||||
Set-Service -Name BITS -StartupType Disabled
|
|
||||||
|
|
||||||
Write-Host "Disabled wuauserv Service."
|
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Disabling wuauserv service."
|
|
||||||
Set-Service -Name wuauserv -StartupType Disabled
|
|
||||||
|
|
||||||
Write-Host "Disabled UsoSvc Service."
|
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Stopping and disabling UsoSvc service."
|
|
||||||
Stop-Service -Name UsoSvc -Force
|
|
||||||
Set-Service -Name UsoSvc -StartupType Disabled
|
|
||||||
|
|
||||||
Remove-Item "C:\Windows\SoftwareDistribution\*" -Recurse -Force
|
|
||||||
Write-Host "Cleared SoftwareDistribution folder."
|
Write-Host "Cleared SoftwareDistribution folder."
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Cleared SoftwareDistribution folder."
|
Write-WinUtilLog -Component "Updates" -Message "Cleared SoftwareDistribution folder."
|
||||||
|
|
||||||
@@ -54,11 +54,11 @@ function Invoke-WPFUpdatesdisable {
|
|||||||
'\Microsoft\WindowsUpdate\*'
|
'\Microsoft\WindowsUpdate\*'
|
||||||
|
|
||||||
foreach ($Task in $Tasks) {
|
foreach ($Task in $Tasks) {
|
||||||
Get-ScheduledTask -TaskPath $Task | Disable-ScheduledTask -ErrorAction SilentlyContinue
|
Get-ScheduledTask -TaskPath $Task -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "=================================" -ForegroundColor Green
|
Write-Host "=================================" -ForegroundColor Green
|
||||||
Write-Host "--- Updates Are Disabled ---" -ForegroundColor Green
|
Write-Host "--- Windows Update Is Disabled ---" -ForegroundColor Green
|
||||||
Write-Host "=================================" -ForegroundColor Green
|
Write-Host "=================================" -ForegroundColor Green
|
||||||
|
|
||||||
Write-Host "Note: You must restart your system in order for all changes to take effect." -ForegroundColor Yellow
|
Write-Host "Note: You must restart your system in order for all changes to take effect." -ForegroundColor Yellow
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ function Invoke-WPFUpdatessecurity {
|
|||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
1. Disables driver offering through Windows Update
|
1. Disables driver offering through Windows Update
|
||||||
2. Disables Windows Update automatic restart
|
2. Defers feature updates for 365 days
|
||||||
3. Sets Windows Update to Semi-Annual Channel (Targeted)
|
3. Defers quality updates for 4 days
|
||||||
4. Defers feature updates for 365 days
|
4. Prevents automatic restarts while a user is signed in
|
||||||
5. Defers quality updates for 4 days
|
|
||||||
|
|
||||||
#>
|
#>
|
||||||
|
|
||||||
@@ -17,6 +16,32 @@ function Invoke-WPFUpdatessecurity {
|
|||||||
Write-WinUtilLog -Component "Updates" -Message "Applying recommended Windows Update settings."
|
Write-WinUtilLog -Component "Updates" -Message "Applying recommended Windows Update settings."
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Disabling driver offering through Windows Update."
|
Write-WinUtilLog -Component "Updates" -Message "Disabling driver offering through Windows Update."
|
||||||
|
|
||||||
|
$windowsUpdatePolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
|
||||||
|
$automaticUpdatePolicyPath = Join-Path $windowsUpdatePolicyPath "AU"
|
||||||
|
|
||||||
|
Write-Host "Restoring Windows Update availability..."
|
||||||
|
Write-WinUtilLog -Component "Updates" -Message "Restoring Windows Update services and scheduled tasks before applying recommended settings."
|
||||||
|
|
||||||
|
Remove-ItemProperty -Path $automaticUpdatePolicyPath -Name "NoAutoUpdate" -ErrorAction SilentlyContinue
|
||||||
|
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Name "DODownloadMode" -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
Set-Service -Name BITS -StartupType Manual
|
||||||
|
Set-Service -Name wuauserv -StartupType Manual
|
||||||
|
Set-Service -Name UsoSvc -StartupType Automatic
|
||||||
|
Start-Service -Name UsoSvc
|
||||||
|
|
||||||
|
$Tasks =
|
||||||
|
'\Microsoft\Windows\InstallService\*',
|
||||||
|
'\Microsoft\Windows\UpdateOrchestrator\*',
|
||||||
|
'\Microsoft\Windows\UpdateAssistant\*',
|
||||||
|
'\Microsoft\Windows\WaaSMedic\*',
|
||||||
|
'\Microsoft\Windows\WindowsUpdate\*',
|
||||||
|
'\Microsoft\WindowsUpdate\*'
|
||||||
|
|
||||||
|
foreach ($Task in $Tasks) {
|
||||||
|
Get-ScheduledTask -TaskPath $Task -ErrorAction SilentlyContinue | Enable-ScheduledTask -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Force
|
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Force
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Name "PreventDeviceMetadataFromNetwork" -Type DWord -Value 1
|
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Name "PreventDeviceMetadataFromNetwork" -Type DWord -Value 1
|
||||||
|
|
||||||
@@ -26,24 +51,30 @@ function Invoke-WPFUpdatessecurity {
|
|||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DontSearchWindowsUpdate" -Type DWord -Value 1
|
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DontSearchWindowsUpdate" -Type DWord -Value 1
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DriverUpdateWizardWuSearchEnabled" -Type DWord -Value 0
|
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DriverUpdateWizardWuSearchEnabled" -Type DWord -Value 0
|
||||||
|
|
||||||
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Force
|
New-Item -Path $windowsUpdatePolicyPath -Force
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Name "ExcludeWUDriversInQualityUpdate" -Type DWord -Value 1
|
Set-ItemProperty -Path $windowsUpdatePolicyPath -Name "ExcludeWUDriversInQualityUpdate" -Type DWord -Value 1
|
||||||
|
|
||||||
Write-Host "Setting cumulative updates back by 1 year and security updates by 4 days..."
|
Write-Host "Deferring feature updates by 365 days and quality updates by 4 days..."
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Deferring feature updates by 365 days and quality updates by 4 days."
|
Write-WinUtilLog -Component "Updates" -Message "Deferring feature updates by 365 days and quality updates by 4 days."
|
||||||
|
|
||||||
New-Item -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Force
|
Set-ItemProperty -Path $windowsUpdatePolicyPath -Name "DeferFeatureUpdates" -Type DWord -Value 1
|
||||||
|
Set-ItemProperty -Path $windowsUpdatePolicyPath -Name "DeferFeatureUpdatesPeriodInDays" -Type DWord -Value 365
|
||||||
|
Set-ItemProperty -Path $windowsUpdatePolicyPath -Name "DeferQualityUpdates" -Type DWord -Value 1
|
||||||
|
Set-ItemProperty -Path $windowsUpdatePolicyPath -Name "DeferQualityUpdatesPeriodInDays" -Type DWord -Value 4
|
||||||
|
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "BranchReadinessLevel" -Type DWord -Value 20
|
$legacySettingsPath = "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings"
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "DeferFeatureUpdatesPeriodInDays" -Type DWord -Value 365
|
foreach ($legacyValue in @("BranchReadinessLevel", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdatesPeriodInDays")) {
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "DeferQualityUpdatesPeriodInDays" -Type DWord -Value 4
|
Remove-ItemProperty -Path $legacySettingsPath -Name $legacyValue -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Disabling Windows Update automatic restart..."
|
Write-Host "Preventing automatic restarts while users are signed in..."
|
||||||
Write-WinUtilLog -Component "Updates" -Message "Disabling Windows Update automatic restart while users are logged in."
|
Write-WinUtilLog -Component "Updates" -Message "Configuring scheduled automatic updates without restarting while users are signed in."
|
||||||
|
|
||||||
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Force
|
New-Item -Path $automaticUpdatePolicyPath -Force
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoRebootWithLoggedOnUsers" -Type DWord -Value 1
|
# NoAutoRebootWithLoggedOnUsers only applies when automatic updates use option 4.
|
||||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "AUPowerManagement" -Type DWord -Value 0
|
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "AUOptions" -Type DWord -Value 4
|
||||||
|
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "NoAutoRebootWithLoggedOnUsers" -Type DWord -Value 1
|
||||||
|
Set-ItemProperty -Path $automaticUpdatePolicyPath -Name "AUPowerManagement" -Type DWord -Value 0
|
||||||
|
|
||||||
Write-Host "================================="
|
Write-Host "================================="
|
||||||
Write-Host "-- Updates Set to Recommended ---"
|
Write-Host "-- Updates Set to Recommended ---"
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ function Invoke-WPFtweaksbutton {
|
|||||||
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
|
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
|
||||||
}
|
}
|
||||||
|
|
||||||
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-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 "================================="
|
||||||
@@ -75,13 +75,13 @@ 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-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-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 "================================="
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ 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-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-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 "=================================="
|
||||||
|
|||||||
+394
-6
@@ -1,12 +1,16 @@
|
|||||||
#===========================================================================
|
#===========================================================================
|
||||||
# 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\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
|
$tokens = $null
|
||||||
$parseErrors = $null
|
$parseErrors = $null
|
||||||
@@ -20,6 +24,16 @@ BeforeAll {
|
|||||||
}, $true)
|
}, $true)
|
||||||
$script:provisionedRemovalScriptBlock = $ps5CommandAssignment.Right.Expression.ScriptBlock.GetScriptBlock()
|
$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)
|
||||||
}
|
}
|
||||||
@@ -29,8 +43,22 @@ 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-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(
|
||||||
@@ -73,6 +101,161 @@ 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-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 { }
|
||||||
@@ -155,13 +338,123 @@ Describe "Remove-WinUtilProvisionedAPPX" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
It "handles child process failures before logging completion" {
|
It "throws after logging child process failures" {
|
||||||
$source = Get-Content -Path $provisionedSourcePath -Raw
|
$source = Get-Content -Path $provisionedSourcePath -Raw
|
||||||
|
|
||||||
$source | Should -Match '\$removalOutput = powershell\.exe .* 2>&1'
|
$source | Should -Match '\$removalOutput = powershell\.exe .* 2>&1'
|
||||||
$source | Should -Match 'if \(\$LASTEXITCODE -ne 0 -or \$null -ne \$removalOutput\)'
|
$source | Should -Match 'Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message \$errorMessage'
|
||||||
$source | Should -Match 'Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "AppX provisioned package removal failed:'
|
$source | Should -Match '(?s)AppX provisioned package removal failed:.*throw \$errorMessage.*AppX provisioned package removal completed\.'
|
||||||
$source | Should -Match '(?s)AppX provisioned package removal failed:.*return.*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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +469,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 }
|
||||||
@@ -189,6 +484,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" {
|
||||||
@@ -203,6 +499,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]@{
|
||||||
@@ -211,7 +522,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
|
||||||
@@ -228,6 +542,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 = @{}
|
||||||
@@ -260,6 +575,8 @@ 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 {
|
||||||
@@ -301,6 +618,77 @@ Describe "Invoke-WPFAppxRemoval runspace body" {
|
|||||||
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
|
||||||
$PackageList.Count -eq 1 -and $PackageList[0] -eq "Example.Package"
|
$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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ BeforeAll {
|
|||||||
function Get-WinUtilSelectedPackages {
|
function Get-WinUtilSelectedPackages {
|
||||||
param($PackageList, [string]$Preference)
|
param($PackageList, [string]$Preference)
|
||||||
}
|
}
|
||||||
function Show-WPFInstallAppBusy {
|
function Set-WinUtilTweaksProgressIndicator {
|
||||||
param($text)
|
param($Visible, $Label, $Percent)
|
||||||
}
|
}
|
||||||
function Hide-WPFInstallAppBusy { }
|
|
||||||
function Install-WinUtilWinget { }
|
function Install-WinUtilWinget { }
|
||||||
function Install-WinUtilChoco { }
|
function Install-WinUtilChoco { }
|
||||||
function Install-WinUtilProgramWinget {
|
function Install-WinUtilProgramWinget {
|
||||||
@@ -74,6 +73,9 @@ BeforeAll {
|
|||||||
preferences = [pscustomobject]@{
|
preferences = [pscustomobject]@{
|
||||||
packagemanager = "Winget"
|
packagemanager = "Winget"
|
||||||
}
|
}
|
||||||
|
Form = [pscustomobject]@{
|
||||||
|
Dispatcher = [pscustomobject]@{}
|
||||||
|
}
|
||||||
configs = @{
|
configs = @{
|
||||||
applicationsHashtable = $applications
|
applicationsHashtable = $applications
|
||||||
}
|
}
|
||||||
@@ -187,8 +189,7 @@ Describe "Invoke-WPFInstall runspace body" {
|
|||||||
Mock Get-WinUtilSelectedPackages {
|
Mock Get-WinUtilSelectedPackages {
|
||||||
New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc")
|
New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc")
|
||||||
}
|
}
|
||||||
Mock Show-WPFInstallAppBusy { }
|
Mock Set-WinUtilTweaksProgressIndicator { }
|
||||||
Mock Hide-WPFInstallAppBusy { }
|
|
||||||
Mock Install-WinUtilWinget { }
|
Mock Install-WinUtilWinget { }
|
||||||
Mock Install-WinUtilChoco { }
|
Mock Install-WinUtilChoco { }
|
||||||
Mock Install-WinUtilProgramWinget { }
|
Mock Install-WinUtilProgramWinget { }
|
||||||
@@ -212,8 +213,17 @@ Describe "Invoke-WPFInstall runspace body" {
|
|||||||
Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter {
|
||||||
@($PackageList).Count -eq 1 -and $Preference -eq "Winget"
|
@($PackageList).Count -eq 1 -and $Preference -eq "Winget"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Show-WPFInstallAppBusy -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
$text -eq "Installing apps..."
|
$Visible -eq $true -and $Label -eq "Preparing app install (0/2)" -and $Percent -eq 0
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "Installed Git.Git (1/2)" -and $Percent -eq 50
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "Installed Chocolatey packages (2/2)" -and $Percent -eq 100
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "App install finished" -and $Percent -eq 100
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Install-WinUtilWinget -Times 1 -Exactly
|
Should -Invoke -CommandName Install-WinUtilWinget -Times 1 -Exactly
|
||||||
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
|
||||||
@@ -223,21 +233,34 @@ Describe "Invoke-WPFInstall runspace body" {
|
|||||||
Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter {
|
||||||
$Action -eq "Install" -and @($Programs)[0] -eq "vlc"
|
$Action -eq "Install" -and @($Programs)[0] -eq "vlc"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
|
||||||
|
}
|
||||||
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
|
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
|
||||||
}
|
}
|
||||||
$script:sync.ProcessRunning | Should -BeFalse
|
$script:sync.ProcessRunning | Should -BeFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
It "hides the busy overlay, sets taskbar error state, and clears ProcessRunning on failure" {
|
It "shows failure progress, sets taskbar error state, and clears ProcessRunning on failure" {
|
||||||
Mock Install-WinUtilProgramWinget { throw "winget failed" }
|
Mock Install-WinUtilProgramWinget { throw "winget failed" }
|
||||||
|
|
||||||
Invoke-WPFInstall
|
Invoke-WPFInstall
|
||||||
|
|
||||||
& $script:capturedInstallScriptBlock -PackagesToInstall @($script:package) -ManagerPreference "Winget"
|
& $script:capturedInstallScriptBlock -PackagesToInstall @($script:package) -ManagerPreference "Winget"
|
||||||
|
|
||||||
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "App install failed" -and $Percent -eq 100
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
|
||||||
|
}
|
||||||
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
|
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
|
||||||
}
|
}
|
||||||
@@ -345,8 +368,7 @@ Describe "Invoke-WPFUnInstall runspace body" {
|
|||||||
Mock Get-WinUtilSelectedPackages {
|
Mock Get-WinUtilSelectedPackages {
|
||||||
New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc")
|
New-WinUtilPackageSplit -Winget @("Git.Git") -Choco @("vlc")
|
||||||
}
|
}
|
||||||
Mock Show-WPFInstallAppBusy { }
|
Mock Set-WinUtilTweaksProgressIndicator { }
|
||||||
Mock Hide-WPFInstallAppBusy { }
|
|
||||||
Mock Install-WinUtilProgramWinget { }
|
Mock Install-WinUtilProgramWinget { }
|
||||||
Mock Install-WinUtilProgramChoco { }
|
Mock Install-WinUtilProgramChoco { }
|
||||||
Mock Invoke-WPFUIThread { }
|
Mock Invoke-WPFUIThread { }
|
||||||
@@ -369,8 +391,17 @@ Describe "Invoke-WPFUnInstall runspace body" {
|
|||||||
Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Get-WinUtilSelectedPackages -Times 1 -Exactly -ParameterFilter {
|
||||||
@($PackageList).Count -eq 1 -and $Preference -eq "Winget"
|
@($PackageList).Count -eq 1 -and $Preference -eq "Winget"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Show-WPFInstallAppBusy -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
$text -eq "Uninstalling apps..."
|
$Visible -eq $true -and $Label -eq "Preparing app uninstall (0/2)" -and $Percent -eq 0
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "Uninstalled Git.Git (1/2)" -and $Percent -eq 50
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "Uninstalled Chocolatey packages (2/2)" -and $Percent -eq 100
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "App uninstall finished" -and $Percent -eq 100
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
|
||||||
$Action -eq "Uninstall" -and @($Programs)[0] -eq "Git.Git"
|
$Action -eq "Uninstall" -and @($Programs)[0] -eq "Git.Git"
|
||||||
@@ -378,21 +409,34 @@ Describe "Invoke-WPFUnInstall runspace body" {
|
|||||||
Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Install-WinUtilProgramChoco -Times 1 -Exactly -ParameterFilter {
|
||||||
$Action -eq "Uninstall" -and @($Programs)[0] -eq "vlc"
|
$Action -eq "Uninstall" -and @($Programs)[0] -eq "vlc"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
|
||||||
|
}
|
||||||
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
|
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
|
||||||
}
|
}
|
||||||
$script:sync.ProcessRunning | Should -BeFalse
|
$script:sync.ProcessRunning | Should -BeFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
It "hides the busy overlay, sets taskbar error state, and clears ProcessRunning on failure" {
|
It "shows failure progress, sets taskbar error state, and clears ProcessRunning on failure" {
|
||||||
Mock Install-WinUtilProgramWinget { throw "winget failed" }
|
Mock Install-WinUtilProgramWinget { throw "winget failed" }
|
||||||
|
|
||||||
Invoke-WPFUnInstall -PackagesToUninstall @($script:package)
|
Invoke-WPFUnInstall -PackagesToUninstall @($script:package)
|
||||||
|
|
||||||
& $script:capturedUninstallScriptBlock -PackagesToUninstall @($script:package) -ManagerPreference "Winget"
|
& $script:capturedUninstallScriptBlock -PackagesToUninstall @($script:package) -ManagerPreference "Winget"
|
||||||
|
|
||||||
Should -Invoke -CommandName Hide-WPFInstallAppBusy -Times 1 -Exactly
|
Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "App uninstall failed" -and $Percent -eq 100
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $false*'
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ScriptBlock.ToString() -like '*$sync.ItemsControl.IsEnabled = $true*'
|
||||||
|
}
|
||||||
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
|
||||||
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
|
$ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,23 @@ Describe "Initialize-WinUtilTabContent" {
|
|||||||
$targetGridName -eq "appxpanel" -and $columncount -eq 2
|
$targetGridName -eq "appxpanel" -and $columncount -eq 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "checks for existing Win11ISO work when the tab is initialized" {
|
||||||
|
Add-Type -AssemblyName WindowsBase
|
||||||
|
$dispatcher = [pscustomobject]@{}
|
||||||
|
$dispatcher | Add-Member -MemberType ScriptMethod -Name BeginInvoke -Value {
|
||||||
|
param($priority, $action)
|
||||||
|
$action.Invoke()
|
||||||
|
}
|
||||||
|
$script:sync.Form = [pscustomobject]@{ Dispatcher = $dispatcher }
|
||||||
|
Mock Invoke-WinUtilISOCheckExistingWork { }
|
||||||
|
|
||||||
|
Initialize-WinUtilTabContent -TabName "Win11ISO"
|
||||||
|
Initialize-WinUtilTabContent -TabName "Win11ISO"
|
||||||
|
|
||||||
|
Should -Invoke -CommandName Invoke-WinUtilISOCheckExistingWork -Times 1 -Exactly
|
||||||
|
$script:sync.InitializedTabs["Win11ISO"] | Should -BeTrue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe "Startup lazy tab wiring" {
|
Describe "Startup lazy tab wiring" {
|
||||||
@@ -96,4 +113,12 @@ Describe "Startup lazy tab wiring" {
|
|||||||
$rendererScript | Should -Match '\$sync\.Buttons\.Add\(\$button\.Name\)'
|
$rendererScript | Should -Match '\$sync\.Buttons\.Add\(\$button\.Name\)'
|
||||||
$mainScript | Should -Match '\$sync\.Buttons -notcontains \$psitem'
|
$mainScript | Should -Match '\$sync\.Buttons -notcontains \$psitem'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "binds generated documentation links when lazy panels are rendered" {
|
||||||
|
$rendererScript = Get-Content -Path (Join-Path $script:repoRoot "functions\public\Invoke-WPFUIElements.ps1") -Raw
|
||||||
|
$mainScript = Get-Content -Path (Join-Path $script:repoRoot "scripts\main.ps1") -Raw
|
||||||
|
|
||||||
|
$rendererScript | Should -Match '(?s)if \(\$entryInfo\.Link\).*\$textBlock\.Add_MouseUp\(\{.*Start-Process \$Sender\.ToolTip -ErrorAction Stop'
|
||||||
|
$mainScript | Should -Not -Match '\.Name\.EndsWith\("Link"\)'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
#===========================================================================
|
||||||
|
# Tests - O&O ShutUp10++ Download Workflow
|
||||||
|
#===========================================================================
|
||||||
|
|
||||||
|
BeforeAll {
|
||||||
|
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||||
|
|
||||||
|
. (Join-Path $script:repoRoot "functions\private\Save-WinUtilFile.ps1")
|
||||||
|
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFOOSU.ps1")
|
||||||
|
|
||||||
|
function Invoke-WPFRunspace {
|
||||||
|
param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock)
|
||||||
|
}
|
||||||
|
function Set-WinUtilTweaksProgressIndicator {
|
||||||
|
param($Visible, $Label, $Percent)
|
||||||
|
}
|
||||||
|
function Show-WinUtilMessage {
|
||||||
|
param($Message, $Title, $Button, $Icon)
|
||||||
|
}
|
||||||
|
function Write-WinUtilLog {
|
||||||
|
param($Message, $Level, $Component)
|
||||||
|
}
|
||||||
|
|
||||||
|
function script:New-WinUtilOOSUTestContext {
|
||||||
|
param([bool]$ProcessRunning = $false)
|
||||||
|
|
||||||
|
$script:sync = [Hashtable]::Synchronized(@{
|
||||||
|
ProcessRunning = $ProcessRunning
|
||||||
|
winutildir = $TestDrive
|
||||||
|
Form = [pscustomobject]@{
|
||||||
|
Dispatcher = [pscustomobject]@{}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Describe "Save-WinUtilFile" {
|
||||||
|
It "copies a download and reports its percentage" {
|
||||||
|
$sourcePath = Join-Path $TestDrive "source.bin"
|
||||||
|
$destinationPath = Join-Path $TestDrive "destination.bin"
|
||||||
|
$sourceBytes = [byte[]](0..255)
|
||||||
|
[System.IO.File]::WriteAllBytes($sourcePath, $sourceBytes)
|
||||||
|
$reportedProgress = [System.Collections.Generic.List[int]]::new()
|
||||||
|
|
||||||
|
Save-WinUtilFile -Uri ([uri]$sourcePath) -DestinationPath $destinationPath -ProgressCallback {
|
||||||
|
param($percent)
|
||||||
|
$reportedProgress.Add($percent)
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.IO.File]::ReadAllBytes($destinationPath) | Should -Be $sourceBytes
|
||||||
|
$reportedProgress[-1] | Should -Be 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Describe "Invoke-WPFOOSU" {
|
||||||
|
BeforeEach {
|
||||||
|
New-WinUtilOOSUTestContext
|
||||||
|
$script:capturedScriptBlock = $null
|
||||||
|
$script:capturedParameterList = $null
|
||||||
|
|
||||||
|
Mock Invoke-WPFRunspace {
|
||||||
|
$script:capturedScriptBlock = $ScriptBlock
|
||||||
|
$script:capturedParameterList = $ParameterList
|
||||||
|
[pscustomobject]@{ MockHandle = $true }
|
||||||
|
}
|
||||||
|
Mock Set-WinUtilTweaksProgressIndicator { }
|
||||||
|
Mock Show-WinUtilMessage { }
|
||||||
|
Mock Write-WinUtilLog { }
|
||||||
|
Mock Start-Process { }
|
||||||
|
Mock Write-Error { }
|
||||||
|
}
|
||||||
|
|
||||||
|
AfterEach {
|
||||||
|
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name capturedScriptBlock -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name capturedParameterList -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
It "queues the download in a background runspace" {
|
||||||
|
Invoke-WPFOOSU
|
||||||
|
|
||||||
|
$script:sync.ProcessRunning | Should -BeTrue
|
||||||
|
Should -Invoke Invoke-WPFRunspace -Times 1 -Exactly
|
||||||
|
$script:capturedParameterList[0][0] | Should -Be "downloadPath"
|
||||||
|
$script:capturedParameterList[0][1] | Should -Be (Join-Path $TestDrive "ooshutup10.exe")
|
||||||
|
}
|
||||||
|
|
||||||
|
It "does not start while another process is running" {
|
||||||
|
New-WinUtilOOSUTestContext -ProcessRunning $true
|
||||||
|
|
||||||
|
Invoke-WPFOOSU
|
||||||
|
|
||||||
|
Should -Invoke Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Message -eq "Another process is currently running." -and
|
||||||
|
$Title -eq "WinUtil" -and
|
||||||
|
$Button -eq "OK" -and
|
||||||
|
$Icon -eq "Warning"
|
||||||
|
}
|
||||||
|
Should -Not -Invoke Invoke-WPFRunspace
|
||||||
|
}
|
||||||
|
|
||||||
|
It "maps download progress to the window indicator and launches O&O ShutUp10++" {
|
||||||
|
Mock Save-WinUtilFile {
|
||||||
|
& $ProgressCallback 35
|
||||||
|
& $ProgressCallback 100
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-WPFOOSU
|
||||||
|
& $script:capturedScriptBlock -downloadPath $script:capturedParameterList[0][1]
|
||||||
|
|
||||||
|
Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "Downloading O&O ShutUp10++ (0%)" -and $Percent -eq 0
|
||||||
|
}
|
||||||
|
Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "Downloading O&O ShutUp10++ (35%)" -and $Percent -eq 35
|
||||||
|
}
|
||||||
|
Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "O&O ShutUp10++ launched" -and $Percent -eq 100
|
||||||
|
}
|
||||||
|
Should -Invoke Start-Process -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$FilePath -eq (Join-Path $TestDrive "ooshutup10.exe")
|
||||||
|
}
|
||||||
|
$script:sync.ProcessRunning | Should -BeFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
It "shows failure progress and clears the running state when the download fails" {
|
||||||
|
Mock Save-WinUtilFile { throw "download failed" }
|
||||||
|
|
||||||
|
Invoke-WPFOOSU
|
||||||
|
& $script:capturedScriptBlock -downloadPath $script:capturedParameterList[0][1]
|
||||||
|
|
||||||
|
Should -Invoke Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Visible -eq $true -and $Label -eq "O&O ShutUp10++ download failed" -and $Percent -eq 100
|
||||||
|
}
|
||||||
|
Should -Not -Invoke Start-Process
|
||||||
|
Should -Invoke Write-Error -Times 1 -Exactly
|
||||||
|
$script:sync.ProcessRunning | Should -BeFalse
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,6 +140,10 @@ Describe "Invoke-WPFRunspace behavior" {
|
|||||||
$runspaceScript | Should -Not -Match '\$script:powershell'
|
$runspaceScript | Should -Not -Match '\$script:powershell'
|
||||||
$runspaceScript | Should -Not -Match '\$script:handle'
|
$runspaceScript | Should -Not -Match '\$script:handle'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "exposes a strongly typed cleanup callback" {
|
||||||
|
([WinUtilRunspaceCleanup]::Callback -is [System.Threading.WaitOrTimerCallback]) | Should -BeTrue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe "Public runspace callers" {
|
Describe "Public runspace callers" {
|
||||||
|
|||||||
@@ -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" {
|
||||||
|
|||||||
@@ -6,12 +6,82 @@ $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|||||||
|
|
||||||
BeforeAll {
|
BeforeAll {
|
||||||
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||||
|
. (Join-Path $script:repoRoot "functions\private\Invoke-WinUtilCurrentSystem.ps1")
|
||||||
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilRegistry.ps1")
|
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilRegistry.ps1")
|
||||||
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilService.ps1")
|
. (Join-Path $script:repoRoot "functions\private\Set-WinUtilService.ps1")
|
||||||
|
|
||||||
|
function winget {
|
||||||
|
param([Parameter(ValueFromRemainingArguments = $true)]$Arguments)
|
||||||
|
}
|
||||||
|
function choco {
|
||||||
|
param([Parameter(ValueFromRemainingArguments = $true)]$Arguments)
|
||||||
|
}
|
||||||
function Write-WinUtilLog { }
|
function Write-WinUtilLog { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Describe "Invoke-WinUtilCurrentSystem installed apps" {
|
||||||
|
BeforeEach {
|
||||||
|
$script:sync = [Hashtable]::Synchronized(@{
|
||||||
|
configs = [pscustomobject]@{
|
||||||
|
applicationsHashtable = @{
|
||||||
|
WPFInstallGit = [pscustomobject]@{ winget = "Git.Git"; choco = "git" }
|
||||||
|
WPFInstallChatGPT = [pscustomobject]@{ winget = "msstore:9NT1R1C2HH7J"; choco = "na" }
|
||||||
|
WPFInstallMissing = [pscustomobject]@{ winget = "Git"; choco = "missing" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Mock winget {
|
||||||
|
$global:LASTEXITCODE = 0
|
||||||
|
$script:wingetArguments = @($Arguments)
|
||||||
|
@(
|
||||||
|
"Name Id Version Source",
|
||||||
|
"--------------------------------",
|
||||||
|
"Git Git.Git 2.0 winget",
|
||||||
|
"ChatGPT 9NT1R1C2HH7J 1.0 msstore"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Mock choco {
|
||||||
|
$script:chocoArguments = @($Arguments)
|
||||||
|
@("Chocolatey v2", "git 2.0", "2 packages installed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AfterEach {
|
||||||
|
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name wingetArguments -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name chocoArguments -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
It "matches single standard and Microsoft Store package IDs" {
|
||||||
|
$result = @(Invoke-WinUtilCurrentSystem -CheckBox "winget")
|
||||||
|
|
||||||
|
$result | Should -HaveCount 2
|
||||||
|
$result | Should -Contain "WPFInstallGit"
|
||||||
|
$result | Should -Contain "WPFInstallChatGPT"
|
||||||
|
$result | Should -Not -Contain "WPFInstallMissing"
|
||||||
|
Should -Invoke -CommandName winget -Times 1 -Exactly
|
||||||
|
$script:wingetArguments | Should -Be @("list", "--accept-source-agreements", "--disable-interactivity")
|
||||||
|
}
|
||||||
|
|
||||||
|
It "fails promptly when Winget cannot list applications" {
|
||||||
|
Mock winget {
|
||||||
|
$global:LASTEXITCODE = 1
|
||||||
|
"winget failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Invoke-WinUtilCurrentSystem -CheckBox "winget" } | Should -Throw "winget list failed with exit code 1."
|
||||||
|
}
|
||||||
|
|
||||||
|
It "matches the primary Chocolatey package ID in one list call" {
|
||||||
|
$result = @(Invoke-WinUtilCurrentSystem -CheckBox "choco")
|
||||||
|
|
||||||
|
$result | Should -Be @("WPFInstallGit")
|
||||||
|
Should -Invoke -CommandName choco -Times 1 -Exactly
|
||||||
|
$script:chocoArguments | Should -Be @("list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Describe "Set-WinUtilRegistry" {
|
Describe "Set-WinUtilRegistry" {
|
||||||
BeforeEach {
|
BeforeEach {
|
||||||
$script:testPathResults = @{}
|
$script:testPathResults = @{}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ BeforeAll {
|
|||||||
function Invoke-WPFUIThread {
|
function Invoke-WPFUIThread {
|
||||||
param([scriptblock]$ScriptBlock)
|
param([scriptblock]$ScriptBlock)
|
||||||
}
|
}
|
||||||
function Set-WinUtilProgressBar {
|
function Set-WinUtilTweaksProgressIndicator {
|
||||||
param($Label, $Percent)
|
param($Visible, $Label, $Percent)
|
||||||
}
|
}
|
||||||
function Write-WinUtilLog {
|
function Write-WinUtilLog {
|
||||||
param($Message, $Level, $Component)
|
param($Message, $Level, $Component)
|
||||||
@@ -184,7 +184,6 @@ Describe "Invoke-WPFtweaksbutton" {
|
|||||||
Mock Invoke-WPFRunspace { [pscustomobject]@{ MockHandle = $true } }
|
Mock Invoke-WPFRunspace { [pscustomobject]@{ MockHandle = $true } }
|
||||||
Mock Invoke-WinUtilTweaks { }
|
Mock Invoke-WinUtilTweaks { }
|
||||||
Mock Invoke-WPFUIThread { }
|
Mock Invoke-WPFUIThread { }
|
||||||
Mock Set-WinUtilProgressBar { }
|
|
||||||
Mock Write-WinUtilLog { }
|
Mock Write-WinUtilLog { }
|
||||||
Mock Write-Host { }
|
Mock Write-Host { }
|
||||||
}
|
}
|
||||||
|
|||||||
+202
-1
@@ -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
|
||||||
@@ -43,9 +61,33 @@ 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-WPFGetInstalled.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-WinUtilTweaksProgressIndicator {
|
||||||
|
param($Visible, $Label, $Percent)
|
||||||
|
}
|
||||||
|
function Invoke-WPFRunspace {
|
||||||
|
param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock)
|
||||||
|
}
|
||||||
|
function Invoke-WPFUIThread {
|
||||||
|
param([scriptblock]$ScriptBlock)
|
||||||
|
}
|
||||||
|
function Invoke-WinUtilCurrentSystem {
|
||||||
|
param($CheckBox)
|
||||||
|
}
|
||||||
|
function Set-WinUtilTaskbaritem {
|
||||||
|
param($state)
|
||||||
|
}
|
||||||
|
function Test-WinUtilPackageManager {
|
||||||
|
param([switch]$winget)
|
||||||
|
}
|
||||||
|
function Write-WinUtilLog {
|
||||||
|
param($Message, $Level, $Component)
|
||||||
|
}
|
||||||
|
|
||||||
function script:New-WinUtilFakeCheckBox {
|
function script:New-WinUtilFakeCheckBox {
|
||||||
param([bool]$IsChecked = $false)
|
param([bool]$IsChecked = $false)
|
||||||
|
|
||||||
@@ -157,6 +199,9 @@ Describe "Invoke-WPFSelectedCheckboxesUpdate" {
|
|||||||
@($script:sync.selectedToggles) | Should -Be @("WPFToggleDarkMode")
|
@($script:sync.selectedToggles) | Should -Be @("WPFToggleDarkMode")
|
||||||
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
||||||
@($script:sync.selectedAppx) | Should -Be @("WPFAppxExample")
|
@($script:sync.selectedAppx) | Should -Be @("WPFAppxExample")
|
||||||
|
$script:sync.WPFselectedAppsButton.Content | Should -Be "Selected Apps: 1"
|
||||||
|
$script:sync.selectedAppsstackPanel.Children.Count | Should -Be 1
|
||||||
|
$script:sync.selectedAppsstackPanel.Children[0].Key | Should -Be "WPFInstallGit"
|
||||||
}
|
}
|
||||||
|
|
||||||
It "removes checkbox keys from the matching selected lists" {
|
It "removes checkbox keys from the matching selected lists" {
|
||||||
@@ -177,6 +222,94 @@ Describe "Invoke-WPFSelectedCheckboxesUpdate" {
|
|||||||
$script:sync.selectedToggles.Count | Should -Be 0
|
$script:sync.selectedToggles.Count | Should -Be 0
|
||||||
$script:sync.selectedFeatures.Count | Should -Be 0
|
$script:sync.selectedFeatures.Count | Should -Be 0
|
||||||
$script:sync.selectedAppx.Count | Should -Be 0
|
$script:sync.selectedAppx.Count | Should -Be 0
|
||||||
|
$script:sync.WPFselectedAppsButton.Content | Should -Be "Selected Apps: 0"
|
||||||
|
$script:sync.selectedAppsstackPanel.Children.Count | Should -Be 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Describe "Invoke-WPFGetInstalled selection state" {
|
||||||
|
BeforeEach {
|
||||||
|
New-WinUtilUiStateTestContext
|
||||||
|
|
||||||
|
$script:sync.ProcessRunning = $false
|
||||||
|
$script:sync.ChocoRadioButton = [pscustomobject]@{ IsChecked = $false }
|
||||||
|
$script:sync.preferences = [pscustomobject]@{ packagemanager = "Winget" }
|
||||||
|
$script:sync.WPFInstallGit = New-WinUtilFakeCheckBox
|
||||||
|
$dispatcher = [pscustomobject]@{}
|
||||||
|
$dispatcher | Add-Member -MemberType ScriptMethod -Name BeginInvoke -Value {
|
||||||
|
param($Action, [object[]]$Arguments)
|
||||||
|
$Action.DynamicInvoke($Arguments)
|
||||||
|
}
|
||||||
|
$script:sync.Form = [pscustomobject]@{ Dispatcher = $dispatcher }
|
||||||
|
$script:capturedGetInstalledScriptBlock = $null
|
||||||
|
$script:capturedGetInstalledParameters = @{}
|
||||||
|
|
||||||
|
Mock Test-WinUtilPackageManager { "installed" }
|
||||||
|
Mock Invoke-WinUtilCurrentSystem { @("WPFInstallGit") }
|
||||||
|
Mock Set-WinUtilTaskbaritem { }
|
||||||
|
Mock Write-WinUtilLog { }
|
||||||
|
Mock Write-Warning { }
|
||||||
|
Mock Invoke-WPFRunspace {
|
||||||
|
$script:capturedGetInstalledScriptBlock = $ScriptBlock
|
||||||
|
foreach ($parameter in $ParameterList) {
|
||||||
|
$script:capturedGetInstalledParameters[$parameter[0]] = $parameter[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AfterEach {
|
||||||
|
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name capturedGetInstalledScriptBlock -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
Remove-Variable -Name capturedGetInstalledParameters -Scope Script -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
It "updates the selected app model, checkbox, count, and popup" {
|
||||||
|
Invoke-WPFGetInstalled -CheckBox "winget"
|
||||||
|
& $script:capturedGetInstalledScriptBlock `
|
||||||
|
-checkbox "winget" `
|
||||||
|
-managerPreference "Winget" `
|
||||||
|
-operation $script:capturedGetInstalledParameters.operation `
|
||||||
|
-completeAction $script:capturedGetInstalledParameters.completeAction
|
||||||
|
|
||||||
|
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
|
||||||
|
$script:sync.WPFInstallGit.IsChecked | Should -BeTrue
|
||||||
|
$script:sync.WPFselectedAppsButton.Content | Should -Be "Selected Apps: 1"
|
||||||
|
$script:sync.selectedAppsstackPanel.Children.Count | Should -Be 1
|
||||||
|
$script:sync.selectedAppsstackPanel.Children[0].Key | Should -Be "WPFInstallGit"
|
||||||
|
}
|
||||||
|
|
||||||
|
It "clears the running state when detection fails" {
|
||||||
|
Mock Invoke-WinUtilCurrentSystem { throw "detection failed" }
|
||||||
|
|
||||||
|
Invoke-WPFGetInstalled -CheckBox "winget"
|
||||||
|
& $script:capturedGetInstalledScriptBlock `
|
||||||
|
-checkbox "winget" `
|
||||||
|
-managerPreference "Winget" `
|
||||||
|
-operation $script:capturedGetInstalledParameters.operation `
|
||||||
|
-completeAction $script:capturedGetInstalledParameters.completeAction
|
||||||
|
|
||||||
|
$script:sync.ProcessRunning | Should -BeFalse
|
||||||
|
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Level -eq "ERROR" -and
|
||||||
|
$Component -eq "Install" -and
|
||||||
|
$Message -eq "Get installed state failed: detection failed"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTaskbaritem -Times 1 -Exactly -ParameterFilter { $state -eq "None" }
|
||||||
|
}
|
||||||
|
|
||||||
|
It "clears the running state when the worker cannot be queued" {
|
||||||
|
Mock Invoke-WPFRunspace { throw "queue failed" }
|
||||||
|
|
||||||
|
Invoke-WPFGetInstalled -CheckBox "winget"
|
||||||
|
|
||||||
|
$script:sync.ProcessRunning | Should -BeFalse
|
||||||
|
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Level -eq "ERROR" -and
|
||||||
|
$Component -eq "Install" -and
|
||||||
|
$Message -eq "Get installed state failed: queue failed"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-WinUtilTaskbaritem -Times 1 -Exactly -ParameterFilter { $state -eq "None" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,3 +405,71 @@ Describe "Invoke-WPFToggleAllCategories" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Describe "Invoke-WPFButton progress cleanup" {
|
||||||
|
BeforeEach {
|
||||||
|
New-WinUtilUiStateTestContext
|
||||||
|
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-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-WinUtilTweaksProgressIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
It "leaves progress visible while a Win11 ISO process is running" {
|
||||||
|
$script:sync.ProcessRunning = $false
|
||||||
|
$script:sync.Win11ISOProcessRunning = $true
|
||||||
|
|
||||||
|
Invoke-WPFButton -Button "WPFNoOp"
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ BeforeAll {
|
|||||||
function Write-WinUtilLog {
|
function Write-WinUtilLog {
|
||||||
param($Message, $Level, $Component)
|
param($Message, $Level, $Component)
|
||||||
}
|
}
|
||||||
|
function Show-WinUtilMessage {
|
||||||
|
param($Message, $Title, $Button, $Icon)
|
||||||
|
}
|
||||||
function Get-ScheduledTask {
|
function Get-ScheduledTask {
|
||||||
param($TaskPath)
|
param($TaskPath, $ErrorAction)
|
||||||
}
|
}
|
||||||
function Disable-ScheduledTask {
|
function Disable-ScheduledTask {
|
||||||
param(
|
param(
|
||||||
@@ -51,6 +54,7 @@ Describe "Invoke-WPFUpdatesdisable" {
|
|||||||
BeforeEach {
|
BeforeEach {
|
||||||
Mock Write-Host { }
|
Mock Write-Host { }
|
||||||
Mock Write-WinUtilLog { }
|
Mock Write-WinUtilLog { }
|
||||||
|
Mock Show-WinUtilMessage { "Yes" }
|
||||||
Mock New-Item { }
|
Mock New-Item { }
|
||||||
Mock Set-ItemProperty { }
|
Mock Set-ItemProperty { }
|
||||||
Mock Set-Service { }
|
Mock Set-Service { }
|
||||||
@@ -88,25 +92,23 @@ Describe "Invoke-WPFUpdatesdisable" {
|
|||||||
$Type -eq "DWord" -and
|
$Type -eq "DWord" -and
|
||||||
$Value -eq 0
|
$Value -eq 0
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
|
||||||
$Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -and
|
|
||||||
$Name -eq "SettingsPageVisibility" -and
|
|
||||||
$Value -eq "hide:windowsupdate"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
It "disables update services and clears the SoftwareDistribution folder" {
|
It "disables update services and clears the SoftwareDistribution folder" {
|
||||||
Invoke-WPFUpdatesdisable
|
Invoke-WPFUpdatesdisable
|
||||||
|
|
||||||
|
foreach ($expectedServiceName in @("BITS", "wuauserv", "UsoSvc")) {
|
||||||
|
$expected = $expectedServiceName
|
||||||
|
Should -Invoke -CommandName Stop-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Name -eq $expected -and $Force -eq $true
|
||||||
|
}
|
||||||
|
}
|
||||||
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
$Name -eq "BITS" -and $StartupType -eq "Disabled"
|
$Name -eq "BITS" -and $StartupType -eq "Disabled"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
$Name -eq "wuauserv" -and $StartupType -eq "Disabled"
|
$Name -eq "wuauserv" -and $StartupType -eq "Disabled"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Stop-Service -Times 1 -Exactly -ParameterFilter {
|
|
||||||
$Name -eq "UsoSvc" -and $Force -eq $true
|
|
||||||
}
|
|
||||||
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
$Name -eq "UsoSvc" -and $StartupType -eq "Disabled"
|
$Name -eq "UsoSvc" -and $StartupType -eq "Disabled"
|
||||||
}
|
}
|
||||||
@@ -128,6 +130,21 @@ Describe "Invoke-WPFUpdatesdisable" {
|
|||||||
}
|
}
|
||||||
Should -Invoke -CommandName Disable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly
|
Should -Invoke -CommandName Disable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "requires confirmation before disabling updates" {
|
||||||
|
Mock Show-WinUtilMessage { "No" }
|
||||||
|
|
||||||
|
Invoke-WPFUpdatesdisable
|
||||||
|
|
||||||
|
Should -Invoke Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Title -eq "Disable Windows Update?" -and
|
||||||
|
$Button -eq "YesNo" -and
|
||||||
|
$Icon -eq "Warning"
|
||||||
|
}
|
||||||
|
Should -Not -Invoke Set-ItemProperty
|
||||||
|
Should -Not -Invoke Set-Service
|
||||||
|
Should -Not -Invoke Remove-Item
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe "Invoke-WPFUpdatesdefault" {
|
Describe "Invoke-WPFUpdatesdefault" {
|
||||||
@@ -136,6 +153,11 @@ Describe "Invoke-WPFUpdatesdefault" {
|
|||||||
Mock Write-WinUtilLog { }
|
Mock Write-WinUtilLog { }
|
||||||
Mock Remove-Item { }
|
Mock Remove-Item { }
|
||||||
Mock Remove-ItemProperty { }
|
Mock Remove-ItemProperty { }
|
||||||
|
Mock Get-ItemProperty {
|
||||||
|
[pscustomobject]@{
|
||||||
|
SettingsPageVisibility = "hide:windowsupdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
Mock Set-Service { }
|
Mock Set-Service { }
|
||||||
Mock Start-Service { }
|
Mock Start-Service { }
|
||||||
Mock Get-ScheduledTask {
|
Mock Get-ScheduledTask {
|
||||||
@@ -147,30 +169,49 @@ Describe "Invoke-WPFUpdatesdefault" {
|
|||||||
Mock secedit { }
|
Mock secedit { }
|
||||||
}
|
}
|
||||||
|
|
||||||
It "removes update policy registry paths and shows the Windows Update settings page" {
|
It "removes only registry values managed by WinUtil" {
|
||||||
Invoke-WPFUpdatesdefault
|
Invoke-WPFUpdatesdefault
|
||||||
|
|
||||||
$expectedPaths = @(
|
$expectedRegistryValues = @(
|
||||||
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU",
|
@("HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU", "NoAutoUpdate", "AUOptions", "NoAutoRebootWithLoggedOnUsers", "AUPowerManagement"),
|
||||||
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization",
|
@("HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate", "ExcludeWUDriversInQualityUpdate", "DeferFeatureUpdates", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdates", "DeferQualityUpdatesPeriodInDays"),
|
||||||
"HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings",
|
@("HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings", "BranchReadinessLevel", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdatesPeriodInDays"),
|
||||||
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata",
|
@("HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata", "PreventDeviceMetadataFromNetwork"),
|
||||||
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching",
|
@("HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching", "DontPromptForWindowsUpdate", "DontSearchWindowsUpdate", "DriverUpdateWizardWuSearchEnabled"),
|
||||||
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
|
@("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config", "DODownloadMode")
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($expectedRegistryPath in $expectedPaths) {
|
foreach ($expectedEntry in $expectedRegistryValues) {
|
||||||
$expected = $expectedRegistryPath
|
$expectedPath = $expectedEntry[0]
|
||||||
Should -Invoke -CommandName Remove-Item -Times 1 -Exactly -ParameterFilter {
|
foreach ($expectedName in $expectedEntry[1..($expectedEntry.Count - 1)]) {
|
||||||
$Path -eq $expected -and $Recurse -eq $true -and $Force -eq $true
|
$valueName = $expectedName
|
||||||
|
Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Path -eq $expectedPath -and $Name -eq $valueName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Should -Not -Invoke Remove-Item
|
||||||
Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
$Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -and
|
$Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -and
|
||||||
$Name -eq "SettingsPageVisibility"
|
$Name -eq "SettingsPageVisibility"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "preserves unrelated Settings page visibility policy" {
|
||||||
|
Mock Get-ItemProperty {
|
||||||
|
[pscustomobject]@{
|
||||||
|
SettingsPageVisibility = "hide:privacy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-WPFUpdatesdefault
|
||||||
|
|
||||||
|
Should -Not -Invoke Remove-ItemProperty -ParameterFilter {
|
||||||
|
$Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -and
|
||||||
|
$Name -eq "SettingsPageVisibility"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
It "restores update service startup types" {
|
It "restores update service startup types" {
|
||||||
Invoke-WPFUpdatesdefault
|
Invoke-WPFUpdatesdefault
|
||||||
|
|
||||||
@@ -186,12 +227,12 @@ Describe "Invoke-WPFUpdatesdefault" {
|
|||||||
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
$Name -eq "UsoSvc" -and $StartupType -eq "Automatic"
|
$Name -eq "UsoSvc" -and $StartupType -eq "Automatic"
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
Should -Not -Invoke Set-Service -ParameterFilter {
|
||||||
$Name -eq "WaaSMedicSvc" -and $StartupType -eq "Manual"
|
$Name -eq "WaaSMedicSvc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
It "enables update scheduled task paths and resets local policy defaults" {
|
It "enables update scheduled task paths without resetting unrelated local security policy" {
|
||||||
Invoke-WPFUpdatesdefault
|
Invoke-WPFUpdatesdefault
|
||||||
|
|
||||||
foreach ($expectedTaskPath in $script:updateTaskPaths) {
|
foreach ($expectedTaskPath in $script:updateTaskPaths) {
|
||||||
@@ -201,13 +242,7 @@ Describe "Invoke-WPFUpdatesdefault" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Enable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly
|
Should -Invoke -CommandName Enable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly
|
||||||
Should -Invoke -CommandName secedit -Times 1 -Exactly -ParameterFilter {
|
Should -Not -Invoke secedit
|
||||||
$Arguments[0] -eq "/configure" -and
|
|
||||||
$Arguments[1] -eq "/cfg" -and
|
|
||||||
$Arguments[2] -like "*\inf\defltbase.inf" -and
|
|
||||||
$Arguments[3] -eq "/db" -and
|
|
||||||
$Arguments[4] -eq "defltbase.sdb"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +252,47 @@ Describe "Invoke-WPFUpdatessecurity" {
|
|||||||
Mock Write-WinUtilLog { }
|
Mock Write-WinUtilLog { }
|
||||||
Mock New-Item { }
|
Mock New-Item { }
|
||||||
Mock Set-ItemProperty { }
|
Mock Set-ItemProperty { }
|
||||||
|
Mock Remove-ItemProperty { }
|
||||||
|
Mock Set-Service { }
|
||||||
|
Mock Start-Service { }
|
||||||
|
Mock Get-ScheduledTask {
|
||||||
|
[pscustomobject]@{
|
||||||
|
TaskPath = $TaskPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mock Enable-ScheduledTask { }
|
||||||
|
}
|
||||||
|
|
||||||
|
It "restores update availability before applying recommended settings" {
|
||||||
|
Invoke-WPFUpdatessecurity
|
||||||
|
|
||||||
|
Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||||
|
$Name -eq "NoAutoUpdate"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Path -eq "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -and
|
||||||
|
$Name -eq "DODownloadMode"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Name -eq "BITS" -and $StartupType -eq "Manual"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Name -eq "wuauserv" -and $StartupType -eq "Manual"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Name -eq "UsoSvc" -and $StartupType -eq "Automatic"
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Start-Service -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Name -eq "UsoSvc"
|
||||||
|
}
|
||||||
|
foreach ($expectedTaskPath in $script:updateTaskPaths) {
|
||||||
|
$expected = $expectedTaskPath
|
||||||
|
Should -Invoke -CommandName Get-ScheduledTask -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$TaskPath -eq $expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Enable-ScheduledTask -Times $script:updateTaskPaths.Count -Exactly
|
||||||
}
|
}
|
||||||
|
|
||||||
It "disables driver metadata and Windows Update driver search" {
|
It "disables driver metadata and Windows Update driver search" {
|
||||||
@@ -261,23 +337,35 @@ Describe "Invoke-WPFUpdatessecurity" {
|
|||||||
Invoke-WPFUpdatessecurity
|
Invoke-WPFUpdatessecurity
|
||||||
|
|
||||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
$Path -eq "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -and
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and
|
||||||
$Name -eq "BranchReadinessLevel" -and
|
$Name -eq "DeferFeatureUpdates" -and
|
||||||
$Type -eq "DWord" -and
|
$Type -eq "DWord" -and
|
||||||
$Value -eq 20
|
$Value -eq 1
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
$Path -eq "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -and
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and
|
||||||
$Name -eq "DeferFeatureUpdatesPeriodInDays" -and
|
$Name -eq "DeferFeatureUpdatesPeriodInDays" -and
|
||||||
$Type -eq "DWord" -and
|
$Type -eq "DWord" -and
|
||||||
$Value -eq 365
|
$Value -eq 365
|
||||||
}
|
}
|
||||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
$Path -eq "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -and
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and
|
||||||
|
$Name -eq "DeferQualityUpdates" -and
|
||||||
|
$Type -eq "DWord" -and
|
||||||
|
$Value -eq 1
|
||||||
|
}
|
||||||
|
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -and
|
||||||
$Name -eq "DeferQualityUpdatesPeriodInDays" -and
|
$Name -eq "DeferQualityUpdatesPeriodInDays" -and
|
||||||
$Type -eq "DWord" -and
|
$Type -eq "DWord" -and
|
||||||
$Value -eq 4
|
$Value -eq 4
|
||||||
}
|
}
|
||||||
|
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||||
|
$Name -eq "AUOptions" -and
|
||||||
|
$Type -eq "DWord" -and
|
||||||
|
$Value -eq 4
|
||||||
|
}
|
||||||
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
$Path -eq "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -and
|
||||||
$Name -eq "NoAutoRebootWithLoggedOnUsers" -and
|
$Name -eq "NoAutoRebootWithLoggedOnUsers" -and
|
||||||
@@ -291,4 +379,16 @@ Describe "Invoke-WPFUpdatessecurity" {
|
|||||||
$Value -eq 0
|
$Value -eq 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "removes legacy WinUtil deferral values from the unsupported UX settings path" {
|
||||||
|
Invoke-WPFUpdatessecurity
|
||||||
|
|
||||||
|
foreach ($expectedValueName in @("BranchReadinessLevel", "DeferFeatureUpdatesPeriodInDays", "DeferQualityUpdatesPeriodInDays")) {
|
||||||
|
$expected = $expectedValueName
|
||||||
|
Should -Invoke Remove-ItemProperty -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Path -eq "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -and
|
||||||
|
$Name -eq $expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Describe "Win11 Creator setup media" {
|
|||||||
BeforeAll {
|
BeforeAll {
|
||||||
$script:repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
$script:repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||||
$script:isoWorkflowPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISO.ps1"
|
$script:isoWorkflowPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISO.ps1"
|
||||||
|
$script:isoUsbWorkflowPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISOUSB.ps1"
|
||||||
$script:isoScriptPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISOScript.ps1"
|
$script:isoScriptPath = Join-Path $script:repoRoot "functions\private\Invoke-WinUtilISOScript.ps1"
|
||||||
$script:autoUnattendPath = Join-Path $script:repoRoot "tools\autounattend.xml"
|
$script:autoUnattendPath = Join-Path $script:repoRoot "tools\autounattend.xml"
|
||||||
|
|
||||||
@@ -36,6 +37,9 @@ Describe "Win11 Creator setup media" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$script:modifyFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOModify"
|
$script:modifyFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOModify"
|
||||||
|
$script:cleanAndResetFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOCleanAndReset"
|
||||||
|
$script:exportFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Invoke-WinUtilISOExport"
|
||||||
|
$script:writeUsbFunction = Get-WinUtilFunctionText -Path $script:isoUsbWorkflowPath -FunctionName "Invoke-WinUtilISOWriteUSB"
|
||||||
$script:editionIdFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Get-WinUtilEditionIdFromName"
|
$script:editionIdFunction = Get-WinUtilFunctionText -Path $script:isoWorkflowPath -FunctionName "Get-WinUtilEditionIdFromName"
|
||||||
$script:addDriversFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Add-DriversToImage"
|
$script:addDriversFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Add-DriversToImage"
|
||||||
$script:answerFileChildElementFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Get-WinUtilISOScriptChildElement"
|
$script:answerFileChildElementFunction = Get-WinUtilFunctionText -Path $script:isoScriptPath -FunctionName "Get-WinUtilISOScriptChildElement"
|
||||||
@@ -81,6 +85,18 @@ Describe "Win11 Creator setup media" {
|
|||||||
$script:modifyFunction | Should -Not -Match ([regex]::Escape("Reusing existing temp directory"))
|
$script:modifyFunction | Should -Not -Match ([regex]::Escape("Reusing existing temp directory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "tracks every background ISO workflow with the shared busy state" {
|
||||||
|
foreach ($functionText in @(
|
||||||
|
$script:modifyFunction,
|
||||||
|
$script:cleanAndResetFunction,
|
||||||
|
$script:exportFunction,
|
||||||
|
$script:writeUsbFunction
|
||||||
|
)) {
|
||||||
|
$functionText | Should -Match ([regex]::Escape('$sync["Win11ISOProcessRunning"] = $true'))
|
||||||
|
$functionText | Should -Match ([regex]::Escape('$sync["Win11ISOProcessRunning"] = $false'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
It "mounts the copied image file that was verified from the ISO" {
|
It "mounts the copied image file that was verified from the ISO" {
|
||||||
foreach ($expectedText in @(
|
foreach ($expectedText in @(
|
||||||
'$sourceImageFileName = Split-Path $wimPath -Leaf',
|
'$sourceImageFileName = Split-Path $wimPath -Leaf',
|
||||||
|
|||||||
+126
-8
@@ -135,12 +135,23 @@ Describe "XAML document" {
|
|||||||
"WPFTab4BT",
|
"WPFTab4BT",
|
||||||
"WPFTab5BT",
|
"WPFTab5BT",
|
||||||
"SearchBar",
|
"SearchBar",
|
||||||
|
"SearchBarIcon",
|
||||||
"SearchBarClearButton",
|
"SearchBarClearButton",
|
||||||
|
"WPFSearchChips",
|
||||||
|
"WPFSearchChipAll",
|
||||||
|
"WPFSearchChipBrowsers",
|
||||||
|
"WPFSearchChipCommunications",
|
||||||
|
"WPFSearchChipDevelopment",
|
||||||
|
"WPFSearchChipGames",
|
||||||
|
"WPFSearchChipMicrosoftTools",
|
||||||
|
"WPFSearchChipMultimediaTools",
|
||||||
|
"WPFSearchChipProTools",
|
||||||
|
"WPFSearchChipSelfhostedTools",
|
||||||
|
"WPFSearchChipUtilities",
|
||||||
"appscategory",
|
"appscategory",
|
||||||
"appspanel",
|
"appspanel",
|
||||||
"tweakspanel",
|
"tweakspanel",
|
||||||
"featurespanel",
|
"featurespanel",
|
||||||
"updatespanel",
|
|
||||||
"appxpanel",
|
"appxpanel",
|
||||||
"WPFstandard",
|
"WPFstandard",
|
||||||
"WPFminimal",
|
"WPFminimal",
|
||||||
@@ -158,6 +169,7 @@ Describe "XAML document" {
|
|||||||
"WPFSelectAllAppx",
|
"WPFSelectAllAppx",
|
||||||
"WPFClearAppxSelection",
|
"WPFClearAppxSelection",
|
||||||
"WPFBackToTweaks",
|
"WPFBackToTweaks",
|
||||||
|
"WPFInstallSelectedAppx",
|
||||||
"WPFRemoveSelectedAppx"
|
"WPFRemoveSelectedAppx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -167,6 +179,23 @@ Describe "XAML document" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "presents the three Updates profiles with accurate action labels" {
|
||||||
|
$updatesTab = $script:xaml.SelectSingleNode('//*[local-name()="TabItem"][@Name="WPFTab4"]')
|
||||||
|
$profileGrid = $updatesTab.SelectSingleNode('.//*[local-name()="UniformGrid"]')
|
||||||
|
$expectedButtons = @{
|
||||||
|
WPFUpdatessecurity = "Apply Recommended"
|
||||||
|
WPFUpdatesdefault = "Restore Defaults"
|
||||||
|
WPFUpdatesdisable = "Disable Updates"
|
||||||
|
}
|
||||||
|
|
||||||
|
$profileGrid.GetAttribute("Columns") | Should -Be "3"
|
||||||
|
foreach ($buttonName in $expectedButtons.Keys) {
|
||||||
|
$button = $updatesTab.SelectSingleNode(".//*[local-name()='Button'][@Name='$buttonName']")
|
||||||
|
$button.GetAttribute("Content") | Should -Be $expectedButtons[$buttonName]
|
||||||
|
}
|
||||||
|
$updatesTab.SelectSingleNode('.//*[@Name="updatespanel"]') | Should -BeNullOrEmpty
|
||||||
|
}
|
||||||
|
|
||||||
It "contains Win11 Creator controls used by the ISO workflow" {
|
It "contains Win11 Creator controls used by the ISO workflow" {
|
||||||
$xamlNames = @(Get-WinUtilXamlRuntimeNamedControls | ForEach-Object { $_.Name })
|
$xamlNames = @(Get-WinUtilXamlRuntimeNamedControls | ForEach-Object { $_.Name })
|
||||||
$requiredControls = @(
|
$requiredControls = @(
|
||||||
@@ -240,11 +269,106 @@ Describe "XAML document" {
|
|||||||
$openButton.GetAttribute("Content").Trim() | Should -Be "AppX Removal"
|
$openButton.GetAttribute("Content").Trim() | Should -Be "AppX Removal"
|
||||||
$openAppxIndex | Should -Be ($getInstalledIndex + 1)
|
$openAppxIndex | Should -Be ($getInstalledIndex + 1)
|
||||||
$appxTab.SelectSingleNode('.//*[local-name()="Button"][@Name="WPFBackToTweaks"]') | Should -Not -BeNullOrEmpty
|
$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
|
$appxTab.SelectSingleNode('.//*[local-name()="Button"][@Name="WPFRemoveSelectedAppx"]') | Should -Not -BeNullOrEmpty
|
||||||
$buttonSource | Should -Match '"WPFAppxRemoval"\s*\{Invoke-WPFTab "WPFTab6BT"\}'
|
$buttonSource | Should -Match '"WPFAppxRemoval"\s*\{Invoke-WPFTab "WPFTab6BT"\}'
|
||||||
$buttonSource | Should -Match '"WPFBackToTweaks"\s*\{Invoke-WPFTab "WPFTab2BT"\}'
|
$buttonSource | Should -Match '"WPFBackToTweaks"\s*\{Invoke-WPFTab "WPFTab2BT"\}'
|
||||||
|
$buttonSource | Should -Match '"WPFInstallSelectedAppx"\s*\{Invoke-WPFAppxInstall\}'
|
||||||
$tabSource | Should -Match '\$sync\.\$tabNav\.Items\[\$tabNumber\]\.IsSelected = \$true'
|
$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" {
|
||||||
@@ -311,7 +435,6 @@ Describe "XAML and sync wiring" {
|
|||||||
"appPopup",
|
"appPopup",
|
||||||
"appPopupSelectedApp",
|
"appPopupSelectedApp",
|
||||||
"ItemsControl",
|
"ItemsControl",
|
||||||
"InstalledPrograms",
|
|
||||||
"ImportInProgress",
|
"ImportInProgress",
|
||||||
"ScriptsInstallPrograms",
|
"ScriptsInstallPrograms",
|
||||||
"keys",
|
"keys",
|
||||||
@@ -324,20 +447,15 @@ Describe "XAML and sync wiring" {
|
|||||||
"InitializedTabs",
|
"InitializedTabs",
|
||||||
"RenderedAssetCache",
|
"RenderedAssetCache",
|
||||||
"ToggleStatusCache",
|
"ToggleStatusCache",
|
||||||
"InstallAppAreaBorder",
|
|
||||||
"InstallAppAreaScrollViewer",
|
|
||||||
"InstallAppAreaOverlay",
|
|
||||||
"InstallAppAreaOverlayText",
|
|
||||||
"InstallAppRenderQueue",
|
"InstallAppRenderQueue",
|
||||||
"InstallAppEntriesRendered",
|
"InstallAppEntriesRendered",
|
||||||
"ProgressBar",
|
|
||||||
"progressBarTextBlock",
|
|
||||||
"FontScaleFactor",
|
"FontScaleFactor",
|
||||||
"Win11ISOImageInfo",
|
"Win11ISOImageInfo",
|
||||||
"Win11ISODriveLetter",
|
"Win11ISODriveLetter",
|
||||||
"Win11ISOWimPath",
|
"Win11ISOWimPath",
|
||||||
"Win11ISOImagePath",
|
"Win11ISOImagePath",
|
||||||
"Win11ISOModifying",
|
"Win11ISOModifying",
|
||||||
|
"Win11ISOProcessRunning",
|
||||||
"Win11ISOWorkDir",
|
"Win11ISOWorkDir",
|
||||||
"Win11ISOContentsDir",
|
"Win11ISOContentsDir",
|
||||||
"Win11ISOUSBDisks"
|
"Win11ISOUSBDisks"
|
||||||
|
|||||||
+27
-14
@@ -128,9 +128,6 @@ Invoke-WinutilThemeChange -theme $sync.preferences.theme
|
|||||||
$sync.InitializedTabs = @{}
|
$sync.InitializedTabs = @{}
|
||||||
Initialize-WinUtilTabContent -TabName "Install"
|
Initialize-WinUtilTabContent -TabName "Install"
|
||||||
|
|
||||||
# Future implementation: Add Windows Version to updates panel
|
|
||||||
#Invoke-WPFUIElements -configVariable $sync.configs.updates -targetGridName "updatespanel" -columncount 1
|
|
||||||
|
|
||||||
#===========================================================================
|
#===========================================================================
|
||||||
# Store Form Objects In PowerShell
|
# Store Form Objects In PowerShell
|
||||||
#===========================================================================
|
#===========================================================================
|
||||||
@@ -171,15 +168,6 @@ $sync.keys | ForEach-Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($($sync["$psitem"].GetType() | Select-Object -ExpandProperty Name) -eq "TextBlock") {
|
|
||||||
if ($sync["$psitem"].Name.EndsWith("Link")) {
|
|
||||||
$sync["$psitem"].Add_MouseUp({
|
|
||||||
[System.Object]$Sender = $args[0]
|
|
||||||
Start-Process $Sender.ToolTip -ErrorAction Stop
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +273,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) {
|
||||||
@@ -340,7 +329,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
|
||||||
@@ -351,21 +340,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
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ $sync.configs = @{}
|
|||||||
$sync.Buttons = [System.Collections.Generic.List[PSObject]]::new()
|
$sync.Buttons = [System.Collections.Generic.List[PSObject]]::new()
|
||||||
$sync.preferences = @{}
|
$sync.preferences = @{}
|
||||||
$sync.ProcessRunning = $false
|
$sync.ProcessRunning = $false
|
||||||
|
$sync.Win11ISOProcessRunning = $false
|
||||||
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
|
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
|
||||||
$sync.selectedApps = [System.Collections.Generic.List[string]]::new()
|
$sync.selectedApps = [System.Collections.Generic.List[string]]::new()
|
||||||
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
||||||
|
|||||||
+319
-117
@@ -6,7 +6,7 @@
|
|||||||
xmlns:local="clr-namespace:WinUtility"
|
xmlns:local="clr-namespace:WinUtility"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
UseLayoutRounding="True"
|
UseLayoutRounding="True"
|
||||||
WindowStyle="None"
|
WindowStyle="SingleBorderWindow"
|
||||||
Width="Auto"
|
Width="Auto"
|
||||||
Height="Auto"
|
Height="Auto"
|
||||||
MinWidth="800"
|
MinWidth="800"
|
||||||
@@ -248,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}" />
|
||||||
@@ -262,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}}"
|
||||||
@@ -309,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}"/>
|
||||||
@@ -321,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}"/>
|
||||||
@@ -918,12 +950,60 @@
|
|||||||
</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>
|
||||||
|
<!-- Filter Chip Style — used by the Install tab category filter buttons -->
|
||||||
|
<Style x:Key="FilterChipStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||||
|
<Setter Property="Margin" Value="2"/>
|
||||||
|
<Setter Property="Padding" Value="12,0,12,0"/>
|
||||||
|
<Setter Property="Width" Value="Auto"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Name="ChipBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{DynamicResource ButtonBorderThickness}"
|
||||||
|
CornerRadius="{DynamicResource ButtonCornerRadius}"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundPressedColor}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundMouseoverColor}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundSelectedColor}"/>
|
||||||
|
<Setter Property="Foreground" Value="DimGray"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
</Window.Resources>
|
</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="*"/>
|
||||||
@@ -940,10 +1020,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}" >
|
||||||
@@ -951,7 +1031,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}">
|
||||||
@@ -959,7 +1039,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}">
|
||||||
@@ -967,7 +1047,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}">
|
||||||
@@ -975,7 +1055,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}">
|
||||||
@@ -992,13 +1072,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}"
|
||||||
@@ -1006,6 +1085,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}"
|
||||||
@@ -1015,14 +1095,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"
|
||||||
@@ -1030,7 +1110,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"
|
||||||
@@ -1068,7 +1148,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=""
|
Content=""
|
||||||
@@ -1136,7 +1216,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=""/>
|
Content=""/>
|
||||||
@@ -1165,24 +1245,62 @@
|
|||||||
</Popup>
|
</Popup>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Content="−" BorderThickness="0"
|
Content=""
|
||||||
BorderBrush="Transparent"
|
Style="{StaticResource HoverButtonStyle}"
|
||||||
Background="{DynamicResource MainBackgroundColor}"
|
BorderThickness="0"
|
||||||
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
|
BorderBrush="Transparent"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Top"
|
Background="{DynamicResource MainBackgroundColor}"
|
||||||
Margin="0,0,0,0"
|
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
|
||||||
FontFamily="{DynamicResource FontFamily}"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Foreground="{DynamicResource MainForegroundColor}" FontSize="{DynamicResource CloseIconFontSize}" Name="WPFMinimizeButton" />
|
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"
|
||||||
|
FontFamily="Segoe MDL2 Assets"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"
|
||||||
|
FontSize="{DynamicResource CloseIconFontSize}"
|
||||||
|
Name="WPFMaximizeButton">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button" BasedOn="{StaticResource HoverButtonStyle}">
|
||||||
|
<Setter Property="Content" Value=""/>
|
||||||
|
<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=""/>
|
||||||
|
<Setter Property="ToolTip" Value="Restore"/>
|
||||||
|
<Setter Property="AutomationProperties.Name" Value="Restore"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Content="×" BorderThickness="0"
|
Content=""
|
||||||
BorderBrush="Transparent"
|
Style="{StaticResource HoverButtonStyle}"
|
||||||
Background="{DynamicResource MainBackgroundColor}"
|
BorderThickness="0"
|
||||||
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
|
BorderBrush="Transparent"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Top"
|
Background="{DynamicResource MainBackgroundColor}"
|
||||||
Margin="0,0,0,0"
|
Width="{DynamicResource IconButtonSize}" Height="{DynamicResource IconButtonSize}"
|
||||||
FontFamily="{DynamicResource FontFamily}"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Foreground="{DynamicResource MainForegroundColor}" FontSize="{DynamicResource CloseIconFontSize}" Name="WPFCloseButton" />
|
Margin="0"
|
||||||
|
FontFamily="Segoe MDL2 Assets"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"
|
||||||
|
FontSize="{DynamicResource CloseIconFontSize}"
|
||||||
|
ToolTip="Close"
|
||||||
|
AutomationProperties.Name="Close"
|
||||||
|
Name="WPFCloseButton" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -1190,8 +1308,33 @@
|
|||||||
<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">
|
||||||
|
<TextBlock Text="Filters"
|
||||||
|
FontSize="{DynamicResource HeaderFontSize}"
|
||||||
|
FontFamily="{DynamicResource HeaderFontFamily}"
|
||||||
|
Foreground="{DynamicResource LabelboxForegroundColor}"
|
||||||
|
Background="Transparent"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="15,0,8,0"/>
|
||||||
|
<Button Name="WPFSearchChipAll" Content="All" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipBrowsers" Content="Browsers" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipCommunications" Content="Communications" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipDevelopment" Content="Development" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipGames" Content="Games" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipMicrosoftTools" Content="Microsoft Tools" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipMultimediaTools" Content="Multimedia Tools" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipProTools" Content="Pro Tools" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipSelfhostedTools" Content="Selfhosted Tools" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
<Button Name="WPFSearchChipUtilities" Content="Utilities" Style="{StaticResource FilterChipStyle}"/>
|
||||||
|
</WrapPanel>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Margin="{DynamicResource TabContentMargin}">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
@@ -1223,14 +1366,14 @@
|
|||||||
|
|
||||||
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Vertical" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5">
|
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Vertical" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5">
|
||||||
<Label Content="Recommended Selections:" FontSize="{DynamicResource FontSize}" VerticalAlignment="Center" Margin="2"/>
|
<Label Content="Recommended Selections:" FontSize="{DynamicResource FontSize}" VerticalAlignment="Center" Margin="2"/>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
|
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
|
||||||
<Button Name="WPFstandard" Content=" Standard " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
<Button Name="WPFstandard" Content=" Standard " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||||
<Button Name="WPFminimal" Content=" Minimal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
<Button Name="WPFminimal" Content=" Minimal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||||
<Button Name="WPFAdvanced" Content=" Advanced " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
<Button Name="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}"/>
|
<Button Name="WPFAppxRemoval" Content=" AppX Removal " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||||
</StackPanel>
|
</WrapPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Grid Name="tweakspanel" Grid.Row="1">
|
<Grid Name="tweakspanel" Grid.Row="1">
|
||||||
@@ -1263,99 +1406,148 @@
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Updates" Visibility="Collapsed" Name="WPFTab4">
|
<TabItem Header="Updates" Visibility="Collapsed" Name="WPFTab4">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="{DynamicResource TabContentMargin}">
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="{DynamicResource TabContentMargin}">
|
||||||
<Grid Background="Transparent" MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ScrollViewer}}">
|
<Grid Background="Transparent" MaxWidth="1250" HorizontalAlignment="Center">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/> <!-- Row for the 3 columns -->
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/> <!-- Row for Windows Version -->
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Three columns container -->
|
<StackPanel Grid.Row="0" Margin="10,10,10,14">
|
||||||
<Grid Grid.Row="0">
|
<TextBlock Text="Windows Update Profiles"
|
||||||
<Grid.ColumnDefinitions>
|
FontSize="24"
|
||||||
<ColumnDefinition Width="*"/>
|
FontWeight="Bold"
|
||||||
<ColumnDefinition Width="*"/>
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<TextBlock Text="Choose how Windows receives updates. Each profile replaces the Windows Update settings managed by WinUtil."
|
||||||
</Grid.ColumnDefinitions>
|
Margin="0,6,0,0"
|
||||||
|
FontSize="13"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Default Settings -->
|
<UniformGrid Grid.Row="1" Columns="3">
|
||||||
<Border Grid.Column="0" Style="{StaticResource BorderStyle}">
|
<Border Style="{StaticResource BorderStyle}"
|
||||||
<StackPanel>
|
BorderBrush="{DynamicResource ProgressBarForegroundColor}"
|
||||||
<Button Name="WPFUpdatesdefault"
|
BorderThickness="2"
|
||||||
FontSize="{DynamicResource ConfigTabButtonFontSize}"
|
Padding="16"
|
||||||
Content="Default Settings"
|
MinHeight="300">
|
||||||
Margin="10,5"
|
<Grid>
|
||||||
Padding="10"/>
|
<Grid.RowDefinitions>
|
||||||
<TextBlock Margin="10"
|
<RowDefinition Height="Auto"/>
|
||||||
TextWrapping="Wrap"
|
<RowDefinition Height="*"/>
|
||||||
Foreground="{DynamicResource MainForegroundColor}">
|
<RowDefinition Height="Auto"/>
|
||||||
<Run FontWeight="Bold">Default Windows Update Configuration</Run>
|
</Grid.RowDefinitions>
|
||||||
<LineBreak/>
|
<StackPanel Grid.Row="0" Margin="0,0,0,14">
|
||||||
- No modifications to Windows defaults
|
<TextBlock Text="Recommended"
|
||||||
<LineBreak/>
|
FontSize="20"
|
||||||
- Removes any custom update settings
|
FontWeight="Bold"
|
||||||
<LineBreak/><LineBreak/>
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
<Run FontStyle="Italic" FontSize="11">Note: This resets your Windows Update settings to default out of the box settings. It removes ANY policy or customization that has been done to Windows Update.</Run>
|
<TextBlock Text="Balanced security and stability"
|
||||||
</TextBlock>
|
Margin="0,4,0,0"
|
||||||
</StackPanel>
|
FontSize="13"
|
||||||
</Border>
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
</StackPanel>
|
||||||
<!-- Security Settings -->
|
<StackPanel Grid.Row="1">
|
||||||
<Border Grid.Column="1" Style="{StaticResource BorderStyle}">
|
<TextBlock Text="- Defers feature updates for 365 days" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
<StackPanel>
|
<TextBlock Text="- Defers quality updates for 4 days" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="- Excludes drivers from quality updates" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="- Prevents automatic restarts while a user is signed in" TextWrapping="Wrap" Margin="0,0,0,12" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="Available on Windows Pro, Enterprise, and Education editions."
|
||||||
|
FontSize="11"
|
||||||
|
FontStyle="Italic"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
</StackPanel>
|
||||||
<Button Name="WPFUpdatessecurity"
|
<Button Name="WPFUpdatessecurity"
|
||||||
|
Grid.Row="2"
|
||||||
|
Content="Apply Recommended"
|
||||||
FontSize="{DynamicResource ConfigTabButtonFontSize}"
|
FontSize="{DynamicResource ConfigTabButtonFontSize}"
|
||||||
Content="Security Settings"
|
Margin="0,16,0,0"
|
||||||
Margin="10,5"
|
|
||||||
Padding="10"/>
|
Padding="10"/>
|
||||||
<TextBlock Margin="10"
|
</Grid>
|
||||||
TextWrapping="Wrap"
|
|
||||||
Foreground="{DynamicResource MainForegroundColor}">
|
|
||||||
<Run FontWeight="Bold">Balanced Security Configuration</Run>
|
|
||||||
<LineBreak/>
|
|
||||||
- Feature updates delayed by 365 days
|
|
||||||
<LineBreak/>
|
|
||||||
- Security updates installed after 4 days
|
|
||||||
<LineBreak/>
|
|
||||||
- Prevents Windows Update from installing drivers
|
|
||||||
<LineBreak/><LineBreak/>
|
|
||||||
<Run FontWeight="SemiBold">Feature Updates:</Run> New features and potential bugs
|
|
||||||
<LineBreak/>
|
|
||||||
<Run FontWeight="SemiBold">Security Updates:</Run> Critical security patches
|
|
||||||
<LineBreak/><LineBreak/>
|
|
||||||
<Run FontStyle="Italic" FontSize="11">Note: This only applies to Pro systems that can use group policy.</Run>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Disable Updates -->
|
<Border Style="{StaticResource BorderStyle}" Padding="16" MinHeight="300">
|
||||||
<Border Grid.Column="2" Style="{StaticResource BorderStyle}">
|
<Grid>
|
||||||
<StackPanel>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,14">
|
||||||
|
<TextBlock Text="Windows Default"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="Return control to Windows"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
FontSize="13"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="1">
|
||||||
|
<TextBlock Text="- Removes Windows Update policies applied by WinUtil" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="- Restores update service startup settings" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="- Re-enables update scheduled tasks" TextWrapping="Wrap" Margin="0,0,0,12" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="Use this to undo the Recommended or Disable profile."
|
||||||
|
FontSize="11"
|
||||||
|
FontStyle="Italic"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<Button Name="WPFUpdatesdefault"
|
||||||
|
Grid.Row="2"
|
||||||
|
Content="Restore Defaults"
|
||||||
|
FontSize="{DynamicResource ConfigTabButtonFontSize}"
|
||||||
|
Margin="0,16,0,0"
|
||||||
|
Padding="10"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Style="{StaticResource BorderStyle}" Padding="16" MinHeight="300">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,14">
|
||||||
|
<TextBlock Text="Disable Updates"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="Red"/>
|
||||||
|
<TextBlock Text="Advanced use only"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
FontSize="13"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="Red"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="1">
|
||||||
|
<TextBlock Text="- Disables automatic update policy" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="- Stops update services and scheduled tasks" TextWrapping="Wrap" Margin="0,0,0,7" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="- Clears downloaded update files" TextWrapping="Wrap" Margin="0,0,0,12" Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
<TextBlock Text="Security updates will not be installed while this profile is active."
|
||||||
|
FontSize="11"
|
||||||
|
FontStyle="Italic"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="Red"/>
|
||||||
|
</StackPanel>
|
||||||
<Button Name="WPFUpdatesdisable"
|
<Button Name="WPFUpdatesdisable"
|
||||||
|
Grid.Row="2"
|
||||||
|
Content="Disable Updates"
|
||||||
FontSize="{DynamicResource ConfigTabButtonFontSize}"
|
FontSize="{DynamicResource ConfigTabButtonFontSize}"
|
||||||
Content="Disable All Updates"
|
|
||||||
Foreground="Red"
|
Foreground="Red"
|
||||||
Margin="10,5"
|
Margin="0,16,0,0"
|
||||||
Padding="10"/>
|
Padding="10"/>
|
||||||
<TextBlock Margin="10"
|
</Grid>
|
||||||
TextWrapping="Wrap"
|
|
||||||
Foreground="{DynamicResource MainForegroundColor}">
|
|
||||||
<Run FontWeight="Bold" Foreground="Red">!! Not Recommended !!</Run>
|
|
||||||
<LineBreak/>
|
|
||||||
- Disables ALL Windows Updates
|
|
||||||
<LineBreak/>
|
|
||||||
- Increases security risks
|
|
||||||
<LineBreak/>
|
|
||||||
- Only use for isolated systems
|
|
||||||
<LineBreak/><LineBreak/>
|
|
||||||
<Run FontStyle="Italic" FontSize="11">Warning: Your system will be vulnerable without security updates.</Run>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</UniformGrid>
|
||||||
|
|
||||||
<!-- Future Implementation: Add Windows Version to updates panel -->
|
<Border Grid.Row="2" Style="{StaticResource BorderStyle}" Margin="8,14,8,8" Padding="12">
|
||||||
<Grid Name="updatespanel" Grid.Row="1" Background="Transparent">
|
<TextBlock Text="Changes apply system-wide. Restart Windows after switching profiles. Use Restore Defaults to undo WinUtil update policies."
|
||||||
</Grid>
|
TextWrapping="Wrap"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource MainForegroundColor}"/>
|
||||||
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
@@ -1700,8 +1892,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>
|
||||||
@@ -1711,11 +1904,20 @@
|
|||||||
<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="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>
|
||||||
|
|
||||||
|
<!-- Window-level 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>
|
||||||
|
|||||||
Reference in New Issue
Block a user