Compare commits

...

9 Commits

Author SHA1 Message Date
Gabi
349889b194 Minor format changes for Invoke-WPFSystemRepair.ps1 (#4100)
* Format changes

* Update Invoke-WPFSystemRepair.ps1
2026-02-22 17:31:54 -06:00
Raphaël
c619f3a830 Add more development tools to applications.json (#4093)
* Add .NET Desktop Runtime 10 to applications.json

* Add LLVM to applications.json

* Add NASM to applications.json
2026-02-22 17:30:43 -06:00
KamaleiZestri
d005a225a2 NoUI mode and Concurrency edits (#4092)
* Autorun changes

* Missed a handle

* Remove todo

---------

Co-authored-by: Chris Titus <dfm.titus@gmail.com>
2026-02-22 17:28:55 -06:00
Gabi
e79e946e76 Remove incorrect infromaiton from WPFTweaksRazerBlock (#4082) 2026-02-22 17:23:19 -06:00
github-actions[bot]
3b957e1a77 Update sponsors in README (#4095)
Co-authored-by: ChrisTitusTech <7896101+ChrisTitusTech@users.noreply.github.com>
2026-02-22 17:21:52 -06:00
Gabi
d078d67b1d Remove debug option (#4094)
* Remove debug option

* Remove debug option
2026-02-22 17:21:32 -06:00
Gabi
191fe95572 Update-KnownIssues.md (#4096)
* Update KnownIssues.md

* Update KnownIssues.md
2026-02-22 17:20:39 -06:00
Sean (ANGRYxScotsman)
d10c9413ac Update devdocs-generator (#4091)
* Update devdocs-generator.ps1

* made new dir

* Update devdocs-generator.ps1

* Update devdocs-generator.ps1
2026-02-22 17:19:43 -06:00
Chris Titus
e93753e5c9 Json-Cleanup-Expansion (#4090)
* initial cleanup

* remove winutilgpu
2026-02-22 17:19:04 -06:00
45 changed files with 483 additions and 397 deletions

View File

@@ -16,13 +16,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Auto-approve PR
run: gh pr review "$PR_NUMBER" --approve
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ secrets.AUTO_MERGE }}
- name: Enable auto-merge
run: gh pr merge "$PR_NUMBER" --squash --auto --delete-branch
env:

View File

@@ -52,7 +52,7 @@ jobs:
run: |
Set-Location tools
./devdocs-generator.ps1
- name: Create Pull Request 🚀
id: cpr
uses: peter-evans/create-pull-request@v6
@@ -66,12 +66,12 @@ jobs:
labels: |
automated
documentation
- name: Check outputs
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- name: Install Node.js dependencies
run: "cd docs && [[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Cache Restore

View File

@@ -33,7 +33,7 @@ jobs:
branch: sponsors-update
delete-branch: true
labels: automated
- name: Check outputs
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"

View File

@@ -1,5 +1,4 @@
param (
[switch]$Debug,
[switch]$Run,
[string]$Arguments
)
@@ -106,17 +105,10 @@ $xaml
$script_content.Add($(Get-Content "scripts\main.ps1"))
if ($Debug) {
Update-Progress "Writing debug files" 95
$appXamlContent | Out-File -FilePath "xaml\inputApp.xaml" -Encoding ascii
$tweaksXamlContent | Out-File -FilePath "xaml\inputTweaks.xaml" -Encoding ascii
$featuresXamlContent | Out-File -FilePath "xaml\inputFeatures.xaml" -Encoding ascii
} else {
Update-Progress "Removing temporary files" 99
Remove-Item "xaml\inputApp.xaml" -ErrorAction SilentlyContinue
Remove-Item "xaml\inputTweaks.xaml" -ErrorAction SilentlyContinue
Remove-Item "xaml\inputFeatures.xaml" -ErrorAction SilentlyContinue
}
Update-Progress "Removing temporary files" 99
Remove-Item "xaml\inputApp.xaml" -ErrorAction SilentlyContinue
Remove-Item "xaml\inputTweaks.xaml" -ErrorAction SilentlyContinue
Remove-Item "xaml\inputFeatures.xaml" -ErrorAction SilentlyContinue
Set-Content -Path "$scriptname" -Value ($script_content -join "`r`n") -Encoding ascii
Write-Progress -Activity "Compiling" -Completed

View File

@@ -76,7 +76,7 @@ You'll see a new file named `winutil.ps1`, which's created by `Compile.ps1` scri
These are the sponsors that help keep this project alive with monthly contributions.
<!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https:&#x2F;&#x2F;github.com&#x2F;dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https:&#x2F;&#x2F;github.com&#x2F;mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https:&#x2F;&#x2F;github.com&#x2F;jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https:&#x2F;&#x2F;github.com&#x2F;robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/KenichiQaz"><img src="https:&#x2F;&#x2F;github.com&#x2F;KenichiQaz.png" width="60px" alt="User avatar: Stefan" /></a><a href="https://github.com/paulsheets"><img src="https:&#x2F;&#x2F;github.com&#x2F;paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https:&#x2F;&#x2F;github.com&#x2F;djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https:&#x2F;&#x2F;github.com&#x2F;anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https:&#x2F;&#x2F;github.com&#x2F;FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https:&#x2F;&#x2F;github.com&#x2F;DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/quaszi"><img src="https:&#x2F;&#x2F;github.com&#x2F;quaszi.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https:&#x2F;&#x2F;github.com&#x2F;DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https:&#x2F;&#x2F;github.com&#x2F;KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https:&#x2F;&#x2F;github.com&#x2F;andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><!-- sponsors -->
<!-- sponsors --><a href="https://github.com/dwelfusius"><img src="https:&#x2F;&#x2F;github.com&#x2F;dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https:&#x2F;&#x2F;github.com&#x2F;mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https:&#x2F;&#x2F;github.com&#x2F;jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https:&#x2F;&#x2F;github.com&#x2F;robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/KenichiQaz"><img src="https:&#x2F;&#x2F;github.com&#x2F;KenichiQaz.png" width="60px" alt="User avatar: Stefan" /></a><a href="https://github.com/paulsheets"><img src="https:&#x2F;&#x2F;github.com&#x2F;paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https:&#x2F;&#x2F;github.com&#x2F;djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https:&#x2F;&#x2F;github.com&#x2F;anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https:&#x2F;&#x2F;github.com&#x2F;FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https:&#x2F;&#x2F;github.com&#x2F;DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/quaszi"><img src="https:&#x2F;&#x2F;github.com&#x2F;quaszi.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https:&#x2F;&#x2F;github.com&#x2F;DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https:&#x2F;&#x2F;github.com&#x2F;KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https:&#x2F;&#x2F;github.com&#x2F;andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/nathanzilgo"><img src="https:&#x2F;&#x2F;github.com&#x2F;nathanzilgo.png" width="60px" alt="User avatar: Nathan Fernandes Pedroza" /></a><!-- sponsors -->
## 🏅 Thanks to all Contributors
Thanks a lot for spending your time helping Winutil grow. Thanks a lot! Keep rocking 🍻.

View File

@@ -496,6 +496,14 @@
"link": "https://dotnet.microsoft.com/download/dotnet/9.0",
"winget": "Microsoft.DotNet.DesktopRuntime.9"
},
"dotnet10": {
"category": "Microsoft Tools",
"choco": "dotnet-10.0-runtime",
"content": ".NET Desktop Runtime 10",
"description": ".NET Desktop Runtime 10 is a runtime environment required for running applications developed with .NET 10.",
"link": "https://dotnet.microsoft.com/download/dotnet/10.0",
"winget": "Microsoft.DotNet.DesktopRuntime.10"
},
"dmt": {
"winget": "GNE.DualMonitorTools",
"choco": "dual-monitor-tools",
@@ -3226,5 +3234,23 @@
"link": "https://zed.dev/",
"winget": "ZedIndustries.Zed",
"foss": true
},
"LLLVM": {
"category": "Development",
"choco": "llvm",
"winget": "LLVM.LLVM",
"description": "A collection of modular and reusable compiler and toolchain technologies.",
"content": "LLVM",
"link": "https://llvm.org",
"foss": true
},
"NASM": {
"category": "Development",
"choco": "nasm",
"winget": "NASM.NASM",
"description": "A powerful assembler for the x86 platform.",
"content": "NASM",
"link": "https://nasm.us",
"foss": true
}
}

View File

@@ -81,4 +81,4 @@
"Order": "6",
"Description": "Toggle the green highlight for FOSS applications"
}
}
}

View File

@@ -4,7 +4,6 @@
"Description": ".NET and .NET Framework is a developer platform made up of tools, programming languages, and libraries for building many different types of applications.",
"category": "Features",
"panel": "1",
"Order": "a010_",
"feature": [
"NetFx4-AdvSrvs",
"NetFx3"
@@ -17,7 +16,6 @@
"Description": "Hyper-V is a hardware virtualization product developed by Microsoft that allows users to create and manage virtual machines.",
"category": "Features",
"panel": "1",
"Order": "a011_",
"feature": [
"Microsoft-Hyper-V-All"
],
@@ -31,7 +29,6 @@
"Description": "Enables legacy programs from previous versions of windows",
"category": "Features",
"panel": "1",
"Order": "a012_",
"feature": [
"WindowsMediaPlayer",
"MediaPlayback",
@@ -46,7 +43,6 @@
"Description": "Windows Subsystem for Linux is an optional feature of Windows that allows Linux programs to run natively on Windows without the need for a separate virtual machine or dual booting.",
"category": "Features",
"panel": "1",
"Order": "a020_",
"feature": [
"VirtualMachinePlatform",
"Microsoft-Windows-Subsystem-Linux"
@@ -59,7 +55,6 @@
"Description": "Network File System (NFS) is a mechanism for storing files on a network.",
"category": "Features",
"panel": "1",
"Order": "a014_",
"feature": [
"ServicesForNFS-ClientOnly",
"ClientForNFS-Infrastructure",
@@ -79,7 +74,6 @@
"Description": "Enables daily registry backup, previously disabled by Microsoft in Windows 10 1803.",
"category": "Features",
"panel": "1",
"Order": "a017_",
"feature": [],
"InvokeScript": [
"
@@ -97,7 +91,6 @@
"Description": "Enables Advanced Boot Options screen that lets you start Windows in advanced troubleshooting modes.",
"category": "Features",
"panel": "1",
"Order": "a018_",
"feature": [],
"InvokeScript": [
"bcdedit /set bootmenupolicy legacy"
@@ -109,7 +102,6 @@
"Description": "Disables Advanced Boot Options screen that lets you start Windows in advanced troubleshooting modes.",
"category": "Features",
"panel": "1",
"Order": "a019_",
"feature": [],
"InvokeScript": [
"bcdedit /set bootmenupolicy standard"
@@ -121,7 +113,6 @@
"Description": "Windows Sandbox is a lightweight virtual machine that provides a temporary desktop environment to safely run applications and programs in isolation.",
"category": "Features",
"panel": "1",
"Order": "a021_",
"feature": [
"Containers-DisposableClientVM"
],
@@ -131,54 +122,54 @@
"Content": "Install Features",
"category": "Features",
"panel": "1",
"Order": "a060_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFFeatureInstall",
"link": "https://winutil.christitus.com/dev/features/features/install"
},
"WPFPanelAutologin": {
"Content": "Set Up Autologin",
"category": "Fixes",
"Order": "a040_",
"panel": "1",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFPanelAutologin",
"link": "https://winutil.christitus.com/dev/features/fixes/autologin"
},
"WPFFixesUpdate": {
"Content": "Reset Windows Update",
"category": "Fixes",
"panel": "1",
"Order": "a041_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFFixesUpdate",
"link": "https://winutil.christitus.com/dev/features/fixes/update"
},
"WPFFixesNetwork": {
"Content": "Reset Network",
"category": "Fixes",
"Order": "a042_",
"panel": "1",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFFixesNetwork",
"link": "https://winutil.christitus.com/dev/features/fixes/network"
},
"WPFPanelDISM": {
"Content": "System Corruption Scan",
"category": "Fixes",
"panel": "1",
"Order": "a043_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFSystemRepair",
"link": "https://winutil.christitus.com/dev/features/fixes/dism"
},
"WPFFixesWinget": {
"Content": "WinGet Reinstall",
"category": "Fixes",
"panel": "1",
"Order": "a044_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFFixesWinget",
"link": "https://winutil.christitus.com/dev/features/fixes/winget"
},
"WPFPanelControl": {
@@ -187,6 +178,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"control"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/control"
},
"WPFPanelComputer": {
@@ -195,6 +189,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"compmgmt.msc"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer"
},
"WPFPanelNetwork": {
@@ -203,6 +200,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"ncpa.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/network"
},
"WPFPanelPower": {
@@ -211,6 +211,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"powercfg.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/power"
},
"WPFPanelPrinter": {
@@ -219,6 +222,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"Start-Process 'shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}'"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/printer"
},
"WPFPanelRegion": {
@@ -227,6 +233,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"intl.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/region"
},
"WPFPanelRestore": {
@@ -235,6 +244,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"rstrui.exe"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore"
},
"WPFPanelSound": {
@@ -243,6 +255,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"mmsys.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/sound"
},
"WPFPanelSystem": {
@@ -251,6 +266,9 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"sysdm.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/system"
},
"WPFPanelTimedate": {
@@ -259,33 +277,36 @@
"panel": "2",
"Type": "Button",
"ButtonWidth": "300",
"InvokeScript": [
"timedate.cpl"
],
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/timedate"
},
"WPFWinUtilInstallPSProfile": {
"Content": "Install CTT PowerShell Profile",
"category": "Powershell Profile Powershell 7+ Only",
"panel": "2",
"Order": "a083_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WinUtilInstallPSProfile",
"link": "https://winutil.christitus.com/dev/features/powershell-profile-powershell-7--only/installpsprofile"
},
"WPFWinUtilUninstallPSProfile": {
"Content": "Uninstall CTT PowerShell Profile",
"category": "Powershell Profile Powershell 7+ Only",
"panel": "2",
"Order": "a084_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WinUtilUninstallPSProfile",
"link": "https://winutil.christitus.com/dev/features/powershell-profile-powershell-7--only/uninstallpsprofile"
},
"WPFWinUtilSSHServer": {
"Content": "Enable OpenSSH Server",
"category": "Remote Access",
"panel": "2",
"Order": "a084_",
"Type": "Button",
"ButtonWidth": "300",
"function": "Invoke-WPFSSHServer",
"link": "https://winutil.christitus.com/dev/features/remote-access/sshserver"
}
}

View File

@@ -66,7 +66,7 @@
"InvokeScript": [
"
# Sometimes if you dont stop the Widgets process the removal may fail
Stop-Process -Name Widgets
Get-AppxPackage Microsoft.WidgetsPlatformRuntime -AllUsers | Remove-AppxPackage -AllUsers
Get-AppxPackage MicrosoftWindows.Client.WebExperience -AllUsers | Remove-AppxPackage -AllUsers
@@ -78,10 +78,10 @@
"UndoScript": [
"
Write-Host \"Restoring widgets AppxPackages\"
Add-AppxPackage -Register \"C:\\Program Files\\WindowsApps\\Microsoft.WidgetsPlatformRuntime*\\AppxManifest.xml\" -DisableDevelopmentMode
Add-AppxPackage -Register \"C:\\Program Files\\WindowsApps\\MicrosoftWindows.Client.WebExperience*\\AppxManifest.xml\" -DisableDevelopmentMode
Invoke-WinUtilExplorerUpdate -action \"restart\"
"
],
@@ -95,12 +95,12 @@
"InvokeScript": [
"
Invoke-WebRequest https://github.com/thebookisclosed/ViVe/releases/download/v0.3.4/ViVeTool-v0.3.4-IntelAmd.zip -OutFile ViVeTool.zip
Expand-Archive ViVeTool.zip
Remove-Item ViVeTool.zip
Start-Process 'ViVeTool\\ViVeTool.exe' -ArgumentList '/disable /id:47205210' -Wait -NoNewWindow
Remove-Item ViVeTool -Recurse
Write-Host 'Old start menu reverted please restart your computer to take effect'
@@ -109,12 +109,12 @@
"UndoScript": [
"
Invoke-WebRequest https://github.com/thebookisclosed/ViVe/releases/download/v0.3.4/ViVeTool-v0.3.4-IntelAmd.zip -OutFile ViVeTool.zip
Expand-Archive ViVeTool.zip
Remove-Item ViVeTool.zip
Start-Process 'ViVeTool\\ViVeTool.exe' -ArgumentList '/enable /id:47205210' -Wait -NoNewWindow
Remove-Item ViVeTool -Recurse
Write-Host 'New start menu reverted please restart your computer to take effect'
@@ -1875,7 +1875,7 @@
Get-AppxPackage -AllUsers Microsoft.MicrosoftOfficeHub | Remove-AppxPackage -AllUsers
$Appx = (Get-AppxPackage MicrosoftWindows.Client.CoreAI).PackageFullName
$Sid = (Get-LocalUser $Env:UserName).Sid.Value
New-Item \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx\\AppxAllUserStore\\EndOfLife\\$Sid\\$Appx\" -Force
Remove-AppxPackage $Appx
@@ -1907,7 +1907,7 @@
},
"WPFTweaksRazerBlock": {
"Content": "Block Razer Software Installs",
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software. WARNING: this will also block all Windows third-party driver installations.",
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
"category": "z__Advanced Tweaks - CAUTION",
"panel": "1",
"registry": [

View File

@@ -27,6 +27,14 @@ If you are still having issues, try using a **VPN**, or changing your **DNS prov
| Cloudflare | `1.1.1.1` | `1.0.0.1` |
| Google | `8.8.8.8` | `8.8.4.4` |
### Script Won't Run
If your PowerShell session is running in **Constrained Language Mode**, some scripts and commands may fail to execute. To check the current language mode, run:
```powershell
$ExecutionContext.SessionState.LanguageMode
```
If it returns `ConstrainedLanguage`, you may need to switch to `FullLanguage` mode or run the script in a session with administrative privileges. Be aware that some security policies may enforce Constrained Language Mode, especially in corporate or managed environments.
### Script blocked by Execution Policy
1. Ensure you are running PowerShell as admin: Press `Windows Key`+`X` and select _PowerShell (Admin)_ in Windows 10, or `Windows Terminal (Admin)` in Windows 11.

View File

@@ -42,3 +42,11 @@ toc: false
### Features
{{< autolinks section="dev/features/features" >}}
### Remote Access
{{< autolinks section="dev/features/remote-access" >}}
### Powershell Profile Powershell 7+ Only
{{< autolinks section="dev/features/powershell-profile-powershell-7--only" >}}

View File

@@ -0,0 +1,7 @@
---
title: "Powershell Profile Powershell 7+ Only"
weight: 5
toc: false
---
{{< autolinks section="dev/features/powershell-profile-powershell-7--only" >}}

View File

@@ -0,0 +1,7 @@
---
title: "Remote Access"
weight: 4
toc: false
---
{{< autolinks section="dev/features/remote-access" >}}

View File

@@ -15,3 +15,11 @@ toc: false
### Features
{{< autolinks section="dev/features/features" >}}
### Remote Access
{{< autolinks section="dev/features/remote-access" >}}
### Powershell Profile Powershell 7+ Only
{{< autolinks section="dev/features/powershell-profile-powershell-7--only" >}}

View File

@@ -12,7 +12,7 @@ description: ""
"InvokeScript": [
"
# Sometimes if you dont stop the Widgets process the removal may fail
Stop-Process -Name Widgets
Get-AppxPackage Microsoft.WidgetsPlatformRuntime -AllUsers | Remove-AppxPackage -AllUsers
Get-AppxPackage MicrosoftWindows.Client.WebExperience -AllUsers | Remove-AppxPackage -AllUsers
@@ -24,10 +24,10 @@ description: ""
"UndoScript": [
"
Write-Host \"Restoring widgets AppxPackages\"
Add-AppxPackage -Register \"C:\\Program Files\\WindowsApps\\Microsoft.WidgetsPlatformRuntime*\\AppxManifest.xml\" -DisableDevelopmentMode
Add-AppxPackage -Register \"C:\\Program Files\\WindowsApps\\MicrosoftWindows.Client.WebExperience*\\AppxManifest.xml\" -DisableDevelopmentMode
Invoke-WinUtilExplorerUpdate -action \"restart\"
"
],

View File

@@ -74,7 +74,7 @@ description: ""
Get-AppxPackage -AllUsers Microsoft.MicrosoftOfficeHub | Remove-AppxPackage -AllUsers
$Appx = (Get-AppxPackage MicrosoftWindows.Client.CoreAI).PackageFullName
$Sid = (Get-LocalUser $Env:UserName).Sid.Value
New-Item \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx\\AppxAllUserStore\\EndOfLife\\$Sid\\$Appx\" -Force
Remove-AppxPackage $Appx

View File

@@ -12,12 +12,12 @@ description: ""
"InvokeScript": [
"
Invoke-WebRequest https://github.com/thebookisclosed/ViVe/releases/download/v0.3.4/ViVeTool-v0.3.4-IntelAmd.zip -OutFile ViVeTool.zip
Expand-Archive ViVeTool.zip
Remove-Item ViVeTool.zip
Start-Process 'ViVeTool\\ViVeTool.exe' -ArgumentList '/disable /id:47205210' -Wait -NoNewWindow
Remove-Item ViVeTool -Recurse
Write-Host 'Old start menu reverted please restart your computer to take effect'
@@ -26,12 +26,12 @@ description: ""
"UndoScript": [
"
Invoke-WebRequest https://github.com/thebookisclosed/ViVe/releases/download/v0.3.4/ViVeTool-v0.3.4-IntelAmd.zip -OutFile ViVeTool.zip
Expand-Archive ViVeTool.zip
Remove-Item ViVeTool.zip
Start-Process 'ViVeTool\\ViVeTool.exe' -ArgumentList '/enable /id:47205210' -Wait -NoNewWindow
Remove-Item ViVeTool -Recurse
Write-Host 'New start menu reverted please restart your computer to take effect'

View File

@@ -15,9 +15,9 @@ function Get-WinUtilSelectedPackages
)
if ($PackageList.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
}
$packages = [System.Collections.Hashtable]::new()

View File

@@ -4,9 +4,9 @@ function Hide-WPFInstallAppBusy {
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.
#>
$sync.form.Dispatcher.Invoke([action]{
Invoke-WPFUIThread -ScriptBlock {
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed
$sync.InstallAppAreaBorder.IsEnabled = $true
$sync.InstallAppAreaScrollViewer.Effect.Radius = 0
})
}
}

View File

@@ -113,7 +113,7 @@ function Install-WinUtilProgramChoco {
[int]$totalPrograms
)
$progressState = if ($currentIndex -eq $totalPrograms) { "Normal" } else { "Error" }
$sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state $progressState -value ($currentIndex / $totalPrograms) })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state $progressState -value ($currentIndex / $totalPrograms) }
}
function Install-ChocoPackage {
@@ -234,7 +234,7 @@ function Install-WinUtilProgramChoco {
for ($currentIndex = 0; $currentIndex -lt $totalPrograms; $currentIndex++) {
$Program = $Programs[$currentIndex]
Set-WinUtilProgressBar -label "$Action $($Program)" -percent ($currentIndex / $totalPrograms * 100)
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($currentIndex / $totalPrograms)})
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($currentIndex / $totalPrograms)}
switch ($Action) {
"Install" {

View File

@@ -117,7 +117,7 @@ Function Install-WinUtilProgramWinget {
$Program = $Programs[$i]
$result = $false
Set-WinUtilProgressBar -label "$Action $($Program)" -percent ($i / $count * 100)
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i / $count)})
Invoke-WPFUIThread -ScriptBlock{ Set-WinUtilTaskbaritem -value ($i / $count)}
$result = switch ($Action) {
"Install" {Invoke-Install -Program $Program}

View File

@@ -8,7 +8,7 @@ function Invoke-WinUtilExplorerUpdate {
)
if ($action -eq "refresh") {
Invoke-WPFRunspace -DebugPreference $DebugPreference -ScriptBlock {
Invoke-WPFRunspace -ScriptBlock {
# Define the Win32 type only if it doesn't exist
if (-not ([System.Management.Automation.PSTypeName]'Win32').Type) {
Add-Type -TypeDefinition @"

View File

@@ -10,46 +10,40 @@ function Invoke-WinUtilFeatureInstall {
$CheckBox
)
$x = 0
if($sync.configs.feature.$CheckBox.feature) {
Foreach( $feature in $sync.configs.feature.$CheckBox.feature ) {
try {
Write-Host "Installing $feature"
Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart
} catch {
if ($CheckBox.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" }
} else {
$CheckBox | ForEach-Object {
if($sync.configs.feature.$psitem.feature) {
Foreach( $feature in $sync.configs.feature.$psitem.feature ) {
try {
Write-Host "Installing $feature"
Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart
} catch {
if ($psitem.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" })
} else {
Write-Warning "Unable to Install $feature due to unhandled exception"
Write-Warning $psitem.Exception.StackTrace
}
Write-Warning "Unable to Install $feature due to unhandled exception"
Write-Warning $CheckBox.Exception.StackTrace
}
}
}
if($sync.configs.feature.$psitem.InvokeScript) {
Foreach( $script in $sync.configs.feature.$psitem.InvokeScript ) {
try {
$Scriptblock = [scriptblock]::Create($script)
}
if($sync.configs.feature.$CheckBox.InvokeScript) {
Foreach( $script in $sync.configs.feature.$CheckBox.InvokeScript ) {
try {
$Scriptblock = [scriptblock]::Create($script)
Write-Host "Running Script for $psitem"
Invoke-Command $scriptblock -ErrorAction stop
} catch {
if ($psitem.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" })
} else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" })
Write-Warning "Unable to Install $feature due to unhandled exception"
Write-Warning $psitem.Exception.StackTrace
}
Write-Host "Running Script for $CheckBox"
Invoke-Command $scriptblock -ErrorAction stop
} catch {
if ($CheckBox.Exception.Message -like "*requires elevation*") {
Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?"
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" }
} else {
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" }
Write-Warning "Unable to Install $feature due to unhandled exception"
Write-Warning $CheckBox.Exception.StackTrace
}
}
}
$X++
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($x/$CheckBox.Count) })
}
}

View File

@@ -1,23 +0,0 @@
function Invoke-WinUtilGPU {
$gpuInfo = Get-CimInstance Win32_VideoController
# GPUs to blacklist from using Demanding Theming
$lowPowerGPUs = (
"*NVIDIA GeForce*M*",
"*NVIDIA GeForce*Laptop*",
"*NVIDIA GeForce*GT*",
"*AMD Radeon(TM)*",
"*Intel(R) HD Graphics*",
"*UHD*"
)
foreach ($gpu in $gpuInfo) {
foreach ($gpuPattern in $lowPowerGPUs) {
if ($gpu.Name -like $gpuPattern) {
return $false
}
}
}
return $true
}

View File

@@ -14,11 +14,15 @@ function Set-WinUtilProgressbar{
[int]$Percent
)
$sync.form.Dispatcher.Invoke([action]{$sync.progressBarTextBlock.Text = $label})
$sync.form.Dispatcher.Invoke([action]{$sync.progressBarTextBlock.ToolTip = $label})
if($PARAM_NOUI) {
return;
}
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.Text = $label}
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.ToolTip = $label}
if ($percent -lt 5 ) {
$percent = 5 # Ensure the progress bar is not empty, as it looks weird
}
$sync.form.Dispatcher.Invoke([action]{ $sync.ProgressBar.Value = $percent})
Invoke-WPFUIThread -ScriptBlock { $sync.ProgressBar.Value = $percent}
}

View File

@@ -10,12 +10,12 @@ function Show-WPFInstallAppBusy {
param (
$text = "Installing apps..."
)
$sync.form.Dispatcher.Invoke([action]{
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 = $text
$sync.InstallAppAreaBorder.IsEnabled = $false
$sync.InstallAppAreaScrollViewer.Effect.Radius = 5
})
}
}

View File

@@ -18,6 +18,31 @@ function Invoke-WPFButton {
Set-WinUtilProgressBar -label "" -percent 0
}
# Check if button is defined in feature config with function or InvokeScript
if ($sync.configs.feature.$Button) {
$buttonConfig = $sync.configs.feature.$Button
# If button has a function defined, call it
if ($buttonConfig.function) {
$functionName = $buttonConfig.function
if (Get-Command $functionName -ErrorAction SilentlyContinue) {
& $functionName
return
}
}
# If button has InvokeScript defined, execute the scripts
if ($buttonConfig.InvokeScript -and $buttonConfig.InvokeScript.Count -gt 0) {
foreach ($script in $buttonConfig.InvokeScript) {
if (-not [string]::IsNullOrWhiteSpace($script)) {
Invoke-Expression $script
}
}
return
}
}
# Fallback to hard-coded switch for buttons not in feature.json
Switch -Wildcard ($Button) {
"WPFTab?BT" {Invoke-WPFTab $Button}
"WPFInstall" {Invoke-WPFInstall}
@@ -34,34 +59,14 @@ function Invoke-WPFButton {
"WPFAddUltPerf" {Invoke-WPFUltimatePerformance -State "Enable"}
"WPFRemoveUltPerf" {Invoke-WPFUltimatePerformance -State "Disable"}
"WPFundoall" {Invoke-WPFundoall}
"WPFFeatureInstall" {Invoke-WPFFeatureInstall}
"WPFPanelDISM" {Invoke-WPFSystemRepair}
"WPFPanelAutologin" {Invoke-WPFPanelAutologin}
"WPFPanelComputer" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelControl" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelNetwork" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelPower" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelPrinter" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelRegion" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelRestore" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelSound" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelSystem" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelTimedate" {Invoke-WPFControlPanel -Panel $button}
"WPFPanelUser" {Invoke-WPFControlPanel -Panel $button}
"WPFUpdatesdefault" {Invoke-WPFUpdatesdefault}
"WPFFixesUpdate" {Invoke-WPFFixesUpdate}
"WPFFixesWinget" {Invoke-WPFFixesWinget}
"WPFRunAdobeCCCleanerTool" {Invoke-WPFRunAdobeCCCleanerTool}
"WPFFixesNetwork" {Invoke-WPFFixesNetwork}
"WPFUpdatesdisable" {Invoke-WPFUpdatesdisable}
"WPFUpdatessecurity" {Invoke-WPFUpdatessecurity}
"WPFWinUtilShortcut" {Invoke-WPFShortcut -ShortcutToAdd "WinUtil" -RunAsAdmin $true}
"WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"}
"WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"}
"WPFCloseButton" {Invoke-WPFCloseButton}
"WPFWinUtilInstallPSProfile" {Invoke-WinUtilInstallPSProfile}
"WPFWinUtilUninstallPSProfile" {Invoke-WinUtilUninstallPSProfile}
"WPFWinUtilSSHServer" {Invoke-WPFSSHServer}
"WPFCloseButton" {$sync.Form.Close(); Write-Host "Bye bye!"}
"WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen}
"WPFToggleFOSSHighlight" {
if ($sync.WPFToggleFOSSHighlight.IsChecked) {

View File

@@ -1,12 +0,0 @@
function Invoke-WPFCloseButton {
<#
.SYNOPSIS
Close application
.PARAMETER Button
#>
$sync["Form"].Close()
Write-Host "Bye bye!"
}

View File

@@ -1,26 +0,0 @@
function Invoke-WPFControlPanel {
<#
.SYNOPSIS
Opens the requested legacy panel
.PARAMETER Panel
The panel to open
#>
param($Panel)
switch ($Panel) {
"WPFPanelControl" {control}
"WPFPanelComputer" {compmgmt.msc}
"WPFPanelNetwork" {ncpa.cpl}
"WPFPanelPower" {powercfg.cpl}
"WPFPanelPrinter" {Start-Process "shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}"}
"WPFPanelRegion" {intl.cpl}
"WPFPanelRestore" {rstrui.exe}
"WPFPanelSound" {mmsys.cpl}
"WPFPanelSystem" {sysdm.cpl}
"WPFPanelTimedate" {timedate.cpl}
"WPFPanelUser" {control userpasswords2}
}
}

View File

@@ -12,21 +12,25 @@ function Invoke-WPFFeatureInstall {
return
}
$Features = $sync.selectedFeatures
Invoke-WPFRunspace -ArgumentList $Features -DebugPreference $DebugPreference -ScriptBlock {
param($Features, $DebugPreference)
$handle = Invoke-WPFRunspace -ScriptBlock {
$Features = $sync.selectedFeatures
$sync.ProcessRunning = $true
if ($Features.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
}
Invoke-WinUtilFeatureInstall $Features
$x = 0
$Features | ForEach-Object {
Invoke-WinUtilFeatureInstall $Feature
$X++
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($x/$CheckBox.Count) }
}
$sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "==================================="
Write-Host "--- Features are Installed ---"

View File

@@ -20,13 +20,13 @@ function Invoke-WPFGetInstalled {
}
$managerPreference = $sync["ManagerPreference"]
Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -DebugPreference $DebugPreference -ScriptBlock {
Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -ScriptBlock {
param (
[string]$checkbox,
[PackageManagers]$managerPreference
)
$sync.ProcessRunning = $true
$sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "Indeterminate" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" }
if ($checkbox -eq "winget") {
Write-Host "Getting Installed Programs..."
@@ -48,6 +48,6 @@ function Invoke-WPFGetInstalled {
Write-Host "Done..."
$sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "None" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" }
}
}

View File

@@ -71,8 +71,10 @@ function Invoke-WPFImpex {
# $flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" }).ForEach({ $_.Value })
$flattenedJson = $jsonFile
Update-WinUtilSelections -flatJson $flattenedJson
# TODO test with toggles
Reset-WPFCheckBoxes -doToggles $true
if (!$PARAM_NOUI) {
Reset-WPFCheckBoxes -doToggles $true
}
}
} catch {
Write-Error "An error occurred while importing: $_"

View File

@@ -1,13 +1,12 @@
function Invoke-WPFInstall {
param (
[Parameter(Mandatory=$false)]
[PSObject[]]$PackagesToInstall = $($sync.selectedApps | Foreach-Object { $sync.configs.applicationsHashtable.$_ })
)
<#
.SYNOPSIS
Installs the selected programs using winget, if one or more of the selected programs are already installed on the system, winget will try and perform an upgrade if there's a newer version to install.
#>
$PackagesToInstall = $sync.selectedApps | Foreach-Object { $sync.configs.applicationsHashtable.$_ }
if($sync.ProcessRunning) {
$msg = "[Invoke-WPFInstall] An Install process is currently running."
[System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
@@ -22,8 +21,8 @@ function Invoke-WPFInstall {
$ManagerPreference = $sync["ManagerPreference"]
Invoke-WPFRunspace -ParameterList @(("PackagesToInstall", $PackagesToInstall),("ManagerPreference", $ManagerPreference)) -DebugPreference $DebugPreference -ScriptBlock {
param($PackagesToInstall, $ManagerPreference, $DebugPreference)
$handle = Invoke-WPFRunspace -ParameterList @(("PackagesToInstall", $PackagesToInstall),("ManagerPreference", $ManagerPreference)) -ScriptBlock {
param($PackagesToInstall, $ManagerPreference)
$packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToInstall -Preference $ManagerPreference
@@ -45,12 +44,12 @@ function Invoke-WPFInstall {
Write-Host "==========================================="
Write-Host "-- Installs have finished ---"
Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
} catch {
Write-Host "==========================================="
Write-Host "Error: $_"
Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" -overlay "warning" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
}
$sync.ProcessRunning = $False
}

View File

@@ -27,8 +27,7 @@ function Invoke-WPFRunspace {
Param (
$ScriptBlock,
$ArgumentList,
$ParameterList,
$DebugPreference
$ParameterList
)
# Create a PowerShell instance
@@ -41,7 +40,7 @@ function Invoke-WPFRunspace {
foreach ($parameter in $ParameterList) {
$script:powershell.AddParameter($parameter[0], $parameter[1])
}
$script:powershell.AddArgument($DebugPreference) # Pass DebugPreference to the script block
$script:powershell.RunspacePool = $sync.runspace
# Execute the RunspacePool

View File

@@ -6,7 +6,7 @@ function Invoke-WPFSSHServer {
#>
Invoke-WPFRunspace -DebugPreference $DebugPreference -ScriptBlock {
Invoke-WPFRunspace -ScriptBlock {
Invoke-WinUtilSSHServer

View File

@@ -2,13 +2,15 @@ function Invoke-WPFSystemRepair {
<#
.SYNOPSIS
Checks for system corruption using SFC, and DISM
Checks for disk failure using Chkdsk
.DESCRIPTION
1. SFC - Fixes system file corruption, and fixes DISM if it was corrupted
2. DISM - Fixes system image corruption, and fixes SFC's system image if it was corrupted
3. Chkdsk - Checks for disk errors, which can cause system file corruption and notifies of early disk failure
1. Chkdsk - Checks for disk errors, which can cause system file corruption and notifies of early disk failure
2. SFC - scans protected system files for corruption and fixes them
3. DISM - Repair a corrupted Windows operating system image
#>
Start-Process cmd.exe -ArgumentList "/c chkdsk.exe /scan /perf" -NoNewWindow -Wait
Start-Process cmd.exe -ArgumentList "/c chkdsk /scan /perf" -NoNewWindow -Wait
Start-Process cmd.exe -ArgumentList "/c sfc /scannow" -NoNewWindow -Wait
Start-Process cmd.exe -ArgumentList "/c dism /online /cleanup-image /restorehealth" -NoNewWindow -Wait

View File

@@ -180,7 +180,7 @@ function Invoke-WPFUIElements {
if ($entryInfo.Checked -eq $true) {
$sync[$entryInfo.Name].IsChecked = $true
}
$sync[$entryInfo.Name].Add_Checked({
Invoke-WPFButton -Button "WPFToggleFOSSHighlight"
})
@@ -189,13 +189,13 @@ function Invoke-WPFUIElements {
})
} else {
$sync[$entryInfo.Name].IsChecked = (Get-WinUtilToggleStatus $entryInfo.Name)
$sync[$entryInfo.Name].Add_Checked({
[System.Object]$Sender = $args[0]
Invoke-WPFSelectedCheckboxesUpdate -type "Add" -checkboxName $Sender.name
Invoke-WinUtilTweaks $Sender.name
})
$sync[$entryInfo.Name].Add_Unchecked({
[System.Object]$Sender = $args[0]
Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkboxName $Sender.name

View File

@@ -0,0 +1,21 @@
function Invoke-WPFUIThread {
<#
.SYNOPSIS
Creates and runs a task on Winutil's WPF Forms thread.
.PARAMETER ScriptBlock
The scriptblock to invoke in the thread
#>
[CmdletBinding()]
Param (
$ScriptBlock
)
if ($PARAM_NOUI) {
return;
}
$sync.form.Dispatcher.Invoke([action]$ScriptBlock)
}

View File

@@ -32,8 +32,8 @@ function Invoke-WPFUnInstall {
$ManagerPreference = $sync["ManagerPreference"]
Invoke-WPFRunspace -ParameterList @(("PackagesToUninstall", $PackagesToUninstall),("ManagerPreference", $ManagerPreference)) -DebugPreference $DebugPreference -ScriptBlock {
param($PackagesToUninstall, $ManagerPreference, $DebugPreference)
Invoke-WPFRunspace -ParameterList @(("PackagesToUninstall", $PackagesToUninstall),("ManagerPreference", $ManagerPreference)) -ScriptBlock {
param($PackagesToUninstall, $ManagerPreference)
$packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToUninstall -Preference $ManagerPreference
$packagesWinget = $packagesSorted[[PackageManagers]::Winget]
@@ -54,12 +54,12 @@ function Invoke-WPFUnInstall {
Write-Host "==========================================="
Write-Host "-- Uninstalls have finished ---"
Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
} catch {
Write-Host "==========================================="
Write-Host "Error: $_"
Write-Host "==========================================="
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" -overlay "warning" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }
}
$sync.ProcessRunning = $False

View File

@@ -25,30 +25,27 @@ function Invoke-WPFtweaksbutton {
Write-Debug "Number of tweaks to process: $($Tweaks.Count)"
# The leading "," in the ParameterList is necessary because we only provide one argument and powershell cannot be convinced that we want a nested loop with only one argument otherwise
Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -DebugPreference $DebugPreference -ScriptBlock {
param(
$tweaks,
$DebugPreference
)
$handle = Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -ScriptBlock {
param($tweaks)
Write-Debug "Inside Number of tweaks to process: $($Tweaks.Count)"
$sync.ProcessRunning = $true
if ($Tweaks.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
}
# Execute other selected tweaks
for ($i = 0; $i -lt $Tweaks.Count; $i++) {
Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100)
Invoke-WinUtilTweaks $tweaks[$i]
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i/$Tweaks.Count) })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$Tweaks.Count) }
}
Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100
$sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "================================="
Write-Host "-- Tweaks are Finished ---"
Write-Host "================================="

View File

@@ -20,26 +20,26 @@ function Invoke-WPFundoall {
return
}
Invoke-WPFRunspace -ArgumentList $tweaks -DebugPreference $DebugPreference -ScriptBlock {
param($tweaks, $DebugPreference)
Invoke-WPFRunspace -ArgumentList $tweaks -ScriptBlock {
param($tweaks)
$sync.ProcessRunning = $true
if ($tweaks.count -eq 1) {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }
} else {
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }
}
for ($i = 0; $i -lt $tweaks.Count; $i++) {
Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100)
Invoke-WinUtiltweaks $tweaks[$i] -undo $true
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) }
}
Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100
$sync.ProcessRunning = $false
$sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" })
Invoke-WPFUIThread -ScriptBlock { Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }
Write-Host "=================================="
Write-Host "--- Undo Tweaks are Finished ---"
Write-Host "=================================="

View File

@@ -0,0 +1,48 @@
function Invoke-WinUtilAutoRun {
<#
.SYNOPSIS
Runs Install, Tweaks, and Features with optional UI invocation.
#>
function BusyWait {
Start-Sleep -Seconds 5
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
}
BusyWait
Write-Host "Applying tweaks..."
Invoke-WPFtweaksbutton
BusyWait
Write-Host "Applying toggles..."
$handle = Invoke-WPFRunspace -ScriptBlock {
$Toggles = $sync.selectedToggles
Write-Debug "Inside Number of toggles to process: $($Toggles.Count)"
$sync.ProcessRunning = $true
for ($i = 0; $i -lt $Tweaks.Count; $i++) {
Invoke-WinUtilTweaks $Toggles[$i]
}
$sync.ProcessRunning = $false
Write-Host "================================="
Write-Host "-- Toggles are Finished ---"
Write-Host "================================="
}
BusyWait
Write-Host "Applying features..."
Invoke-WPFFeatureInstall
BusyWait
Write-Host "Installing applications..."
Invoke-WPFInstall
BusyWait
Write-Host "Done."
}

View File

@@ -13,10 +13,14 @@ $maxthreads = [int]$env:NUMBER_OF_PROCESSORS
# Create a new session state for parsing variables into our runspace
$hashVars = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'sync',$sync,$Null
$debugVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'DebugPreference',$DebugPreference,$Null
$uiVar = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'PARAM_NOUI',$PARAM_NOUI,$Null
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
# Add the variable to the session state
$InitialSessionState.Variables.Add($hashVars)
$InitialSessionState.Variables.Add($debugVar)
$InitialSessionState.Variables.Add($uiVar)
# Get every private function and add them to the session state
$functions = Get-ChildItem function:\ | Where-Object { $_.Name -imatch 'winutil|WPF' }
@@ -55,6 +59,42 @@ class GenericException : Exception {
GenericException($Message) : base($Message) {}
}
# Load the configuration files
$sync.configs.applicationsHashtable = @{}
$sync.configs.applications.PSObject.Properties | ForEach-Object {
$sync.configs.applicationsHashtable[$_.Name] = $_.Value
}
Set-PackageManagerPreference
if ($PARAM_NOUI) {
Show-CTTLogo
if ($PARAM_CONFIG -and -not [string]::IsNullOrWhiteSpace($PARAM_CONFIG)) {
Write-Host "Running config file tasks..."
Invoke-WPFImpex -type "import" -Config $PARAM_CONFIG
if ($PARAM_RUN) {
Invoke-WinUtilAutoRun
}
else {
Write-Host "Did you forget to add '--Run'?";
}
$sync.runspace.Dispose()
$sync.runspace.Close()
[System.GC]::Collect()
Stop-Transcript
exit 1
}
else {
Write-Host "Cannot automatically run without a config file provided."
$sync.runspace.Dispose()
$sync.runspace.Close()
[System.GC]::Collect()
Stop-Transcript
exit 1
}
}
$inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
@@ -115,12 +155,7 @@ $sync.Form.Add_Loaded({
})
Invoke-WinutilThemeChange -init $true
# Load the configuration files
$sync.configs.applicationsHashtable = @{}
$sync.configs.applications.PSObject.Properties | ForEach-Object {
$sync.configs.applicationsHashtable[$_.Name] = $_.Value
}
# Now call the function with the final merged config
Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1
@@ -144,7 +179,6 @@ $xaml.SelectNodes("//*[@Name]") | ForEach-Object {$sync["$("$($psitem.Name)")"]
#Persist Package Manager preference across winutil restarts
$sync.ChocoRadioButton.Add_Checked({Set-PackageManagerPreference Choco})
$sync.WingetRadioButton.Add_Checked({Set-PackageManagerPreference Winget})
Set-PackageManagerPreference
switch ($sync["ManagerPreference"]) {
"Choco" {$sync.ChocoRadioButton.IsChecked = $true; break}
@@ -341,45 +375,11 @@ $sync["Form"].Add_ContentRendered({
$sync["Form"].Focus()
# maybe this is not the best place to load and execute config file?
# maybe community can help?
if ($PARAM_CONFIG -and -not [string]::IsNullOrWhiteSpace($PARAM_CONFIG)) {
if ($PARAM_CONFIG -and -not [string]::IsNullOrWhiteSpace($PARAM_CONFIG)) {
Write-Host "Running config file tasks..."
Invoke-WPFImpex -type "import" -Config $PARAM_CONFIG
if ($PARAM_RUN) {
# Wait for any existing process to complete before starting
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
Start-Sleep -Seconds 5
Write-Host "Applying tweaks..."
if (-not $sync.ProcessRunning) {
Invoke-WPFtweaksbutton
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
}
Start-Sleep -Seconds 5
Write-Host "Installing features..."
if (-not $sync.ProcessRunning) {
Invoke-WPFFeatureInstall
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 5
}
}
Start-Sleep -Seconds 5
Write-Host "Installing applications..."
if (-not $sync.ProcessRunning) {
Invoke-WPFInstall
while ($sync.ProcessRunning) {
Start-Sleep -Seconds 1
}
}
Start-Sleep -Seconds 5
Write-Host "Done."
Invoke-WinUtilAutoRun
}
}

View File

@@ -7,16 +7,11 @@
#>
param (
[switch]$Debug,
[string]$Config,
[switch]$Run
[switch]$Run,
[switch]$Noui
)
# Set DebugPreference based on the -Debug switch
if ($Debug) {
$DebugPreference = "Continue"
}
if ($Config) {
$PARAM_CONFIG = $Config
}
@@ -24,10 +19,14 @@ if ($Config) {
$PARAM_RUN = $false
# Handle the -Run switch
if ($Run) {
Write-Host "Running config file tasks..."
$PARAM_RUN = $true
}
$PARAM_NOUI = $false
if ($Noui) {
$PARAM_NOUI = $true
}
# Load DLLs
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms

View File

@@ -1,9 +1,7 @@
<#
.DESCRIPTION
Generates Hugo-compatible markdown files for the development documentation
based on config/tweaks.json and config/feature.json.
Each JSON entry gets its own .md file with the raw JSON snippet or PowerShell function embedded.
Called by the GitHub Actions docs workflow before Hugo build.
Generates Hugo markdown docs from config/tweaks.json and config/feature.json.
Run by the GitHub Actions docs workflow before Hugo build.
#>
function Update-Progress {
@@ -18,11 +16,7 @@ function Update-Progress {
}
function Get-RawJsonBlock {
<#
.SYNOPSIS
Extracts the raw JSON text for a specific item from a JSON file's lines.
Returns the line number and raw text, excluding the "link" property and closing brace.
#>
# Returns the raw JSON text and 1-based start line for an item, excluding the "link" property.
param (
[Parameter(Mandatory)]
[string]$ItemName,
@@ -32,13 +26,12 @@ function Get-RawJsonBlock {
)
$escapedName = [regex]::Escape($ItemName)
$startIndex = -1
$startIndex = -1
$startIndent = ""
# Find the line containing "ItemName": {
for ($i = 0; $i -lt $JsonLines.Count; $i++) {
if ($JsonLines[$i] -match "^(\s*)`"$escapedName`"\s*:\s*\{") {
$startIndex = $i
$startIndex = $i
$startIndent = $matches[1]
break
}
@@ -49,9 +42,8 @@ function Get-RawJsonBlock {
return $null
}
# Find the closing } at the same indentation level
$escapedIndent = [regex]::Escape($startIndent)
$endIndex = -1
$endIndex = -1
for ($i = ($startIndex + 1); $i -lt $JsonLines.Count; $i++) {
if ($JsonLines[$i] -match "^$escapedIndent\}") {
$endIndex = $i
@@ -64,7 +56,7 @@ function Get-RawJsonBlock {
return $null
}
# Walk backwards from closing brace to exclude "link" property and empty lines
# Strip trailing "link" property and blank lines before returning
$lastContentIndex = $endIndex - 1
while ($lastContentIndex -gt $startIndex) {
$trimmed = $JsonLines[$lastContentIndex].Trim()
@@ -75,28 +67,21 @@ function Get-RawJsonBlock {
}
}
$rawLines = $JsonLines[$startIndex..$lastContentIndex]
$rawText = $rawLines -join "`r`n"
return @{
LineNumber = $startIndex + 1 # 1-based
RawText = $rawText
LineNumber = $startIndex + 1
RawText = ($JsonLines[$startIndex..$lastContentIndex] -join "`r`n")
}
}
function Get-ButtonFunctionMapping {
<#
.SYNOPSIS
Parses Invoke-WPFButton.ps1 to build a hashtable mapping button names to function names.
#>
# Parses Invoke-WPFButton.ps1 and returns a hashtable of button name -> function name.
param (
[Parameter(Mandatory)]
[string]$ButtonFilePath
)
$mapping = @{}
$lines = Get-Content -Path $ButtonFilePath
foreach ($line in $lines) {
foreach ($line in (Get-Content -Path $ButtonFilePath)) {
if ($line -match '^\s*"(\w+)"\s*\{(Invoke-\w+)') {
$mapping[$matches[1]] = $matches[2]
}
@@ -105,11 +90,8 @@ function Get-ButtonFunctionMapping {
}
function Add-LinkAttributeToJson {
<#
.SYNOPSIS
Updates the "link" property on each top-level entry in a JSON config file
to point to the corresponding documentation page URL.
#>
# Updates only the "link" property for each entry in a JSON config file.
# Reads via ConvertFrom-Json for metadata, then edits lines directly to avoid reformatting.
param (
[Parameter(Mandatory)]
[string]$JsonFilePath,
@@ -119,43 +101,85 @@ function Add-LinkAttributeToJson {
[string]$ItemNameToCut
)
$jsonText = Get-Content -Path $JsonFilePath -Raw
$jsonData = $jsonText | ConvertFrom-Json
$jsonData = Get-Content -Path $JsonFilePath -Raw | ConvertFrom-Json
$lines = [System.Collections.Generic.List[string]](Get-Content -Path $JsonFilePath)
foreach ($item in $jsonData.PSObject.Properties) {
$itemName = $item.Name
$itemDetails = $item.Value
$category = $itemDetails.category -replace '[^a-zA-Z0-9]', '-'
$itemName = $item.Name
$category = $item.Value.category -replace '[^a-zA-Z0-9]', '-'
$displayName = $itemName -replace $ItemNameToCut, ''
$docLink = "$UrlPrefix/$($category.ToLower())/$($displayName.ToLower())"
$newLink = "$UrlPrefix/$($category.ToLower())/$($displayName.ToLower())"
$escapedName = [regex]::Escape($itemName)
$itemDetails | Add-Member -NotePropertyName "link" -NotePropertyValue $docLink -Force
# Find item start line
$startIdx = -1
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match "^\s*`"$escapedName`"\s*:\s*\{") {
$startIdx = $i
break
}
}
if ($startIdx -eq -1) { continue }
# Derive indentation: propIndent is one level deeper than the item start.
# Used to target only top-level properties and skip nested object braces.
$null = $lines[$startIdx] -match '^(\s*)'
$propIndent = $matches[1] + ' '
$propIndentLen = $propIndent.Length
$escapedPropIndent = [regex]::Escape($propIndent)
# Scan forward: update existing "link" or find the closing brace to insert one.
# Closing brace is matched by indent <= propIndentLen to handle inconsistent formatting.
$linkUpdated = $false
$closeBraceIdx = -1
for ($j = $startIdx + 1; $j -lt $lines.Count; $j++) {
if ($lines[$j] -match "^$escapedPropIndent`"link`"\s*:") {
$lines[$j] = $lines[$j] -replace '"link"\s*:\s*"[^"]*"', "`"link`": `"$newLink`""
$linkUpdated = $true
break
}
if ($lines[$j] -match '^\s*\}') {
$null = $lines[$j] -match '^(\s*)'
if ($matches[1].Length -le $propIndentLen) {
$closeBraceIdx = $j
break
}
}
}
if (-not $linkUpdated -and $closeBraceIdx -ne -1) {
# Insert "link" before the closing brace
$prevPropIdx = $closeBraceIdx - 1
while ($prevPropIdx -gt $startIdx -and $lines[$prevPropIdx].Trim() -eq '') { $prevPropIdx-- }
if ($lines[$prevPropIdx] -notmatch ',\s*$') {
$lines[$prevPropIdx] = $lines[$prevPropIdx].TrimEnd() + ','
}
$lines.Insert($closeBraceIdx, "$propIndent`"link`": `"$newLink`"")
}
}
$jsonText = ($jsonData | ConvertTo-Json -Depth 100).replace('\n', "`n").replace('\r', "`r")
Set-Content -Path $JsonFilePath -Value $jsonText -Encoding utf8
Set-Content -Path $JsonFilePath -Value $lines -Encoding utf8
}
# ==============================================================================
# Main Script
# Main
# ==============================================================================
# Use PSScriptRoot if available (running as a script file), otherwise assume CWD is tools/
$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path }
$repoRoot = Resolve-Path "$scriptDir/.."
$repoRoot = Resolve-Path "$scriptDir/.."
# Paths
$tweaksJsonPath = "$repoRoot/config/tweaks.json"
$featuresJsonPath = "$repoRoot/config/feature.json"
$tweaksOutputDir = "$repoRoot/docs/content/dev/tweaks"
$featuresOutputDir = "$repoRoot/docs/content/dev/features"
$tweaksJsonPath = "$repoRoot/config/tweaks.json"
$featuresJsonPath = "$repoRoot/config/feature.json"
$tweaksOutputDir = "$repoRoot/docs/content/dev/tweaks"
$featuresOutputDir = "$repoRoot/docs/content/dev/features"
$publicFunctionsDir = "$repoRoot/functions/public"
$privateFunctionsDir = "$repoRoot/functions/private"
$itemnametocut = 'WPF(WinUtil|Toggle|Features?|Tweaks?|Panel|Fix(es)?)?'
$baseUrl = "https://winutil.christitus.com"
$baseUrl = "https://winutil.christitus.com"
# Categories that should have generated documentation
# Categories with generated docs
$documentedCategories = @(
"Essential Tweaks",
"z__Advanced Tweaks - CAUTION",
@@ -163,49 +187,44 @@ $documentedCategories = @(
"Performance Plans",
"Features",
"Fixes",
"Legacy Windows Panels"
"Legacy Windows Panels",
"Powershell Profile Powershell 7+ Only",
"Remote Access"
)
# --- Load data ---
# Categories where Button entries embed a PS function instead of raw JSON
$functionEmbedCategories = @(
"Fixes",
"Powershell Profile Powershell 7+ Only",
"Remote Access"
)
Update-Progress "Loading JSON files" 10
$tweaks = Get-Content -Path $tweaksJsonPath -Raw | ConvertFrom-Json
$tweaks = Get-Content -Path $tweaksJsonPath -Raw | ConvertFrom-Json
$features = Get-Content -Path $featuresJsonPath -Raw | ConvertFrom-Json
# --- Load function files (content + relative path) ---
Update-Progress "Loading function files" 20
$functionFiles = @{}
Get-ChildItem -Path $publicFunctionsDir -Filter *.ps1 | ForEach-Object {
$functionFiles[$_.BaseName] = @{
Content = (Get-Content -Path $_.FullName -Raw).TrimEnd()
RelativePath = "functions/public/$($_.Name)"
}
Get-ChildItem -Path $publicFunctionsDir -Filter *.ps1 | ForEach-Object {
$functionFiles[$_.BaseName] = @{ Content = (Get-Content -Path $_.FullName -Raw).TrimEnd(); RelativePath = "functions/public/$($_.Name)" }
}
Get-ChildItem -Path $privateFunctionsDir -Filter *.ps1 | ForEach-Object {
$functionFiles[$_.BaseName] = @{
Content = (Get-Content -Path $_.FullName -Raw).TrimEnd()
RelativePath = "functions/private/$($_.Name)"
}
$functionFiles[$_.BaseName] = @{ Content = (Get-Content -Path $_.FullName -Raw).TrimEnd(); RelativePath = "functions/private/$($_.Name)" }
}
# --- Build button-to-function mapping ---
Update-Progress "Building button-to-function mapping" 30
$buttonFunctionMap = Get-ButtonFunctionMapping -ButtonFilePath "$publicFunctionsDir/Invoke-WPFButton.ps1"
# --- Update link attributes in JSON files ---
Update-Progress "Updating documentation links in JSON" 40
Add-LinkAttributeToJson -JsonFilePath $tweaksJsonPath -UrlPrefix "$baseUrl/dev/tweaks" -ItemNameToCut $itemnametocut
Add-LinkAttributeToJson -JsonFilePath $featuresJsonPath -UrlPrefix "$baseUrl/dev/features" -ItemNameToCut $itemnametocut
Add-LinkAttributeToJson -JsonFilePath $featuresJsonPath -UrlPrefix "$baseUrl/dev/features" -ItemNameToCut $itemnametocut
# Reload JSON lines after link update (so line numbers are accurate)
# Reload lines after link update so line numbers in docs are accurate
$tweaksLines = Get-Content -Path $tweaksJsonPath
$featuresLines = Get-Content -Path $featuresJsonPath
# ==============================================================================
# Clean up old generated .md files (keep _index.md)
# Clean up old generated .md files (preserve _index.md)
# ==============================================================================
Update-Progress "Cleaning up old generated docs" 45
@@ -221,9 +240,9 @@ foreach ($dir in @($tweaksOutputDir, $featuresOutputDir)) {
Update-Progress "Generating tweak documentation" 50
$tweakNames = $tweaks.PSObject.Properties.Name
$tweakNames = $tweaks.PSObject.Properties.Name
$totalTweaks = $tweakNames.Count
$tweakCount = 0
$tweakCount = 0
foreach ($itemName in $tweakNames) {
$item = $tweaks.$itemName
@@ -236,25 +255,20 @@ foreach ($itemName in $tweakNames) {
$categoryDir = "$tweaksOutputDir/$category"
$filename = "$categoryDir/$displayName.md"
if (-Not (Test-Path -Path $categoryDir)) {
New-Item -ItemType Directory -Path $categoryDir | Out-Null
}
if (-Not (Test-Path -Path $categoryDir)) { New-Item -ItemType Directory -Path $categoryDir | Out-Null }
# Hugo frontmatter
$title = $item.Content -replace '"', '\"'
$title = $item.Content -replace '"', '\"'
$content = "---`r`ntitle: `"$title`"`r`ndescription: `"`"`r`n---`r`n`r`n"
if ($item.Type -eq "Button") {
# Button-type tweak: embed the mapped PowerShell function
$funcName = $buttonFunctionMap[$itemName]
if ($funcName -and $functionFiles.ContainsKey($funcName)) {
$func = $functionFiles[$funcName]
$func = $functionFiles[$funcName]
$content += "``````powershell {filename=`"$($func.RelativePath)`",linenos=inline,linenostart=1}`r`n"
$content += $func.Content + "`r`n"
$content += "```````r`n"
}
} else {
# Standard tweak: embed raw JSON block
$jsonBlock = Get-RawJsonBlock -ItemName $itemName -JsonLines $tweaksLines
if ($jsonBlock) {
$content += "``````json {filename=`"config/tweaks.json`",linenos=inline,linenostart=$($jsonBlock.LineNumber)}`r`n"
@@ -262,27 +276,16 @@ foreach ($itemName in $tweakNames) {
$content += "```````r`n"
}
# Registry Changes section
if ($item.registry) {
$content += "`r`n## Registry Changes`r`n`r`n"
$content += "Applications and System Components store and retrieve configuration data to modify windows settings, so we can use the registry to change many settings in one place.`r`n`r`n"
$content += "You can find information about the registry on [Wikipedia](https://www.wikiwand.com/en/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).`r`n"
}
# Service function reference
if ($item.service -and $functionFiles.ContainsKey("Set-WinUtilService")) {
$svcFunc = $functionFiles["Set-WinUtilService"]
$content += "#Function`r`n"
$content += "``````powershell {filename=`"$($svcFunc.RelativePath)`",linenos=inline,linenostart=1}`r`n"
$content += $svcFunc.Content + "`r`n"
$content += "```````r`n"
}
}
Set-Content -Path $filename -Value $content -Encoding utf8 -NoNewline
$percent = 50 + [int](($tweakCount / $totalTweaks) * 20)
if ($percent -gt 70) { $percent = 70 }
$percent = [Math]::Min(70, 50 + [int](($tweakCount / $totalTweaks) * 20))
Update-Progress "Generating tweak documentation ($tweakCount/$totalTweaks)" $percent
}
@@ -292,17 +295,15 @@ foreach ($itemName in $tweakNames) {
Update-Progress "Generating feature documentation" 70
$featureNames = $features.PSObject.Properties.Name
$featureNames = $features.PSObject.Properties.Name
$totalFeatures = $featureNames.Count
$featureCount = 0
$featureCount = 0
foreach ($itemName in $featureNames) {
$item = $features.$itemName
$featureCount++
if ($item.category -notin $documentedCategories) { continue }
# Skip pure UI buttons that don't need docs
if ($itemName -eq "WPFFeatureInstall") { continue }
$category = $item.category -replace '[^a-zA-Z0-9]', '-'
@@ -310,24 +311,20 @@ foreach ($itemName in $featureNames) {
$categoryDir = "$featuresOutputDir/$category"
$filename = "$categoryDir/$displayName.md"
if (-Not (Test-Path -Path $categoryDir)) {
New-Item -ItemType Directory -Path $categoryDir | Out-Null
}
if (-Not (Test-Path -Path $categoryDir)) { New-Item -ItemType Directory -Path $categoryDir | Out-Null }
$title = $item.Content -replace '"', '\"'
$title = $item.Content -replace '"', '\"'
$content = "---`r`ntitle: `"$title`"`r`ndescription: `"`"`r`n---`r`n`r`n"
if ($item.category -eq "Fixes" -or $item.category -eq "Legacy Windows Panels") {
# Embed the PowerShell function file
$funcName = $buttonFunctionMap[$itemName]
if ($item.category -in $functionEmbedCategories) {
$funcName = if ($item.function) { $item.function } else { $buttonFunctionMap[$itemName] }
if ($funcName -and $functionFiles.ContainsKey($funcName)) {
$func = $functionFiles[$funcName]
$func = $functionFiles[$funcName]
$content += "``````powershell {filename=`"$($func.RelativePath)`",linenos=inline,linenostart=1}`r`n"
$content += $func.Content + "`r`n"
$content += "```````r`n"
}
} else {
# Features category: embed raw JSON block
$jsonBlock = Get-RawJsonBlock -ItemName $itemName -JsonLines $featuresLines
if ($jsonBlock) {
$content += "``````json {filename=`"config/feature.json`",linenos=inline,linenostart=$($jsonBlock.LineNumber)}`r`n"
@@ -338,8 +335,7 @@ foreach ($itemName in $featureNames) {
Set-Content -Path $filename -Value $content -Encoding utf8 -NoNewline
$percent = 70 + [int](($featureCount / $totalFeatures) * 20)
if ($percent -gt 90) { $percent = 90 }
$percent = [Math]::Min(90, 70 + [int](($featureCount / $totalFeatures) * 20))
Update-Progress "Generating feature documentation ($featureCount/$totalFeatures)" $percent
}