mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-08-10 01:51:18 +10:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3b4f85fce | ||
|
|
104c763132 | ||
|
|
8062815655 | ||
|
|
58be808823 | ||
|
|
77dc64cbea | ||
|
|
72d47ba020 | ||
|
|
b6b479685a | ||
|
|
c7dfbeb7fe | ||
|
|
7c71a39f7a | ||
|
|
0c42b9e458 | ||
|
|
26f89adf95 | ||
|
|
5003f14e0a | ||
|
|
8b6098b36a | ||
|
|
1f0756336f | ||
|
|
0f39648de6 | ||
|
|
7f0d2183e3 | ||
|
|
6ae24d071d |
@@ -81,7 +81,7 @@ jobs:
|
||||
run: "cd docs && [[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
|
||||
- name: Cache Restore
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v6
|
||||
with:
|
||||
path: |
|
||||
${{ runner.temp }}/hugo_cache
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
--cacheDir "${{ runner.temp }}/hugo_cache"
|
||||
- name: Cache Save
|
||||
id: cache-save
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v6
|
||||
with:
|
||||
path: |
|
||||
${{ runner.temp }}/hugo_cache
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
# AGENTS.md
|
||||
|
||||
Drop-in operating instructions for coding agents. Read this file before every task.
|
||||
|
||||
**Working code only. Finish the job. Plausibility is not correctness.**
|
||||
|
||||
This repository follows the AGENTS.md convention: these instructions are for Codex, Claude Code, Cursor, Windsurf, Copilot, Aider, Devin, Amp, and other coding agents that read `AGENTS.md`.
|
||||
|
||||
## 0. Non-Negotiables
|
||||
|
||||
These rules override everything else in this file when in conflict:
|
||||
|
||||
1. **Do not edit `winutil.ps1` directly.** It is generated build output. Change source files and compile.
|
||||
2. **Do not commit `winutil.ps1`.** It is ignored locally and generated by GitHub Actions for releases.
|
||||
3. **Never fabricate.** Do not invent file paths, function names, command output, test results, commit hashes, or API behavior. Read the file or run the command.
|
||||
4. **Disagree when the premise is wrong.** Say what is wrong before acting on it.
|
||||
5. **Stop when genuinely ambiguous.** If two interpretations would produce materially different diffs, ask before editing.
|
||||
6. **Touch only what the task requires.** No drive-by refactors, formatting sweeps, or unrelated cleanup.
|
||||
7. **Verify before saying done.** A plausible-looking diff is not proof.
|
||||
|
||||
## 1. Project Context
|
||||
|
||||
WinUtil is a Windows PowerShell utility with a WPF interface. The repository is maintained as modular source, but the distributed artifact is one compiled PowerShell script.
|
||||
|
||||
### Stack
|
||||
|
||||
- Language: Windows PowerShell / PowerShell.
|
||||
- UI: WPF via `xaml/inputXML.xaml`.
|
||||
- Configuration: JSON files under `config/`.
|
||||
- Tests: Pester tests under `pester/`.
|
||||
- Lint: PowerShell Script Analyzer with settings in `lint/PSScriptAnalyser.ps1`.
|
||||
- Docs: Hugo site under `docs/`.
|
||||
- Release artifact: generated root `winutil.ps1`.
|
||||
|
||||
### Key Commands
|
||||
|
||||
- Compile:
|
||||
```powershell
|
||||
.\Compile.ps1
|
||||
```
|
||||
- Compile and run GUI:
|
||||
```powershell
|
||||
.\Compile.ps1 -Run
|
||||
```
|
||||
- Run tests:
|
||||
```powershell
|
||||
Invoke-Pester -Path 'pester/*.Tests.ps1' -Output Detailed
|
||||
```
|
||||
- Run Script Analyzer with project settings when available:
|
||||
```powershell
|
||||
Invoke-ScriptAnalyzer -Path . -Settings .\lint\PSScriptAnalyser.ps1 -Recurse
|
||||
```
|
||||
|
||||
Prefer the narrowest useful verification while iterating. Use the full relevant check before finishing.
|
||||
|
||||
## 2. Source Of Truth
|
||||
|
||||
Make durable changes only in files consumed by `Compile.ps1` or in documentation/test files:
|
||||
|
||||
- `scripts/start.ps1` for startup/bootstrap code.
|
||||
- `functions/public/*.ps1` for UI-facing and user-facing workflows.
|
||||
- `functions/private/*.ps1` for internal helpers.
|
||||
- `config/*.json` for applications, tweaks, features, DNS, presets, navigation, themes, and related declarative data.
|
||||
- `xaml/inputXML.xaml` for the WPF UI layout.
|
||||
- `tools/autounattend.xml` for the embedded unattended Windows setup template.
|
||||
- `scripts/main.ps1` for the final entrypoint and GUI initialization logic appended during compile.
|
||||
- `pester/*.Tests.ps1` for automated checks.
|
||||
- `docs/` for Hugo documentation.
|
||||
|
||||
If behavior changes require the compiled script to change, update these source files and run `.\Compile.ps1` only to verify generation.
|
||||
|
||||
## 3. Build Model
|
||||
|
||||
`Compile.ps1` combines the repository sources into `winutil.ps1` in this order:
|
||||
|
||||
1. Read `scripts/start.ps1` and replace `#{replaceme}` with the current `yy.MM.dd` build date.
|
||||
2. Append every file under `functions/` recursively.
|
||||
3. Convert each `config/*.json` file into embedded `$sync.configs` objects.
|
||||
4. Special-case `config/applications.json` so keys receive the `WPFInstall` prefix in compiled config.
|
||||
5. Embed `xaml/inputXML.xaml` into `$inputXML`.
|
||||
6. Embed `tools/autounattend.xml` into `$WinUtilAutounattendXml`.
|
||||
7. Append `scripts/main.ps1`.
|
||||
8. Write the result to root `winutil.ps1`.
|
||||
|
||||
Because the final script is concatenated, do not rely on runtime module imports or source-relative dot-sourcing unless the compiled script will also contain the required code/data.
|
||||
|
||||
## 4. Before Editing
|
||||
|
||||
- State the plan in one or two sentences before editing. For non-trivial work, include the verification you intend to run.
|
||||
- Read the files you will touch and the files that call them.
|
||||
- Match existing patterns even when a different greenfield design would be cleaner.
|
||||
- Surface assumptions when they affect behavior, compatibility, or user data.
|
||||
- If two approaches have meaningful tradeoffs, name them before choosing. Trivial tasks can proceed directly.
|
||||
|
||||
## 5. Coding Guidelines
|
||||
|
||||
- Prefer the minimum code that solves the stated problem.
|
||||
- Keep PowerShell functions in one function file when practical, with the file name matching the primary function name.
|
||||
- Use approved PowerShell verb-noun names and follow the existing `WPF` / `WinUtil` naming conventions.
|
||||
- Keep UI event handler names aligned with XAML element names. A button named `WPFExampleButton` is typically handled by `Invoke-WPFExampleButton`.
|
||||
- Use `$sync` for shared state and UI references, consistent with the existing runspace model.
|
||||
- Update WPF controls through the UI dispatcher when running work in a background runspace.
|
||||
- Keep config-driven features in JSON when they fit the existing schema instead of hard-coding lists in PowerShell.
|
||||
- Preserve undo/original-state data for tweaks so users can reverse changes.
|
||||
- Do not add abstractions, configurability, hooks, or "future extensibility" unless the task needs them now.
|
||||
- Clean up orphans created by your own changes, such as unused variables or functions made obsolete by the edit.
|
||||
- Avoid broad formatting-only edits, especially in JSON config files, XAML, docs, and generated output.
|
||||
|
||||
## 6. Runtime And Safety Rules
|
||||
|
||||
- WinUtil performs system-level Windows changes. Treat registry, services, AppX removal, package manager, Windows Update, ISO, and unattended setup changes as high-risk.
|
||||
- Prefer existing helper functions for WinGet, Chocolatey, registry, services, progress, and UI updates.
|
||||
- Keep tweaks reversible where the schema supports it by including original values or original states.
|
||||
- Never modify a user's original ISO in-place; follow existing copy/mount/export patterns.
|
||||
- Avoid storing credentials, secrets, or machine-specific paths in repo files.
|
||||
- Preserve logging and user feedback patterns for long-running or destructive operations.
|
||||
|
||||
## 7. Surgical Changes
|
||||
|
||||
- Do not improve adjacent code, comments, formatting, imports, or docs unless required.
|
||||
- Do not refactor working code because you are already in the file.
|
||||
- Do not delete pre-existing dead code unless asked; mention it in the summary if relevant.
|
||||
- Keep diffs reviewable. Every changed line should trace to the user's request.
|
||||
- If a change starts spreading across unrelated areas, pause and reassess the plan.
|
||||
|
||||
## 8. Verification
|
||||
|
||||
Define success in terms that can be checked, then check it.
|
||||
|
||||
- For compile/build changes, run `.\Compile.ps1`.
|
||||
- For GUI behavior changes, run `.\Compile.ps1 -Run` when practical and verify the affected path manually.
|
||||
- For config changes, run the compile check and relevant Pester tests.
|
||||
- For function changes, run the relevant Pester tests or add/update focused tests when practical.
|
||||
- For docs-only changes, proofread the changed files and skip runtime tests unless docs generation is affected.
|
||||
- Read command output. Do not report tests as passing unless they actually passed.
|
||||
- If verification fails, fix the cause rather than weakening the test.
|
||||
|
||||
If a check cannot be run, say exactly why and what residual risk remains.
|
||||
|
||||
## 9. Generated Files And Git Hygiene
|
||||
|
||||
- Treat local `winutil.ps1` changes as disposable compile output.
|
||||
- Never stage or commit `winutil.ps1`, `docs/public/`, `docs/resources/`, `binary/`, editor folders, or other ignored build artifacts.
|
||||
- Do not remove `.gitignore` rules that keep generated artifacts out of Git.
|
||||
- Before finishing, check `git status --short` and separate your changes from pre-existing user changes.
|
||||
- Do not revert user changes unless explicitly asked.
|
||||
- Commit messages, when requested, should be descriptive: short subject under 72 characters, body explaining why when needed.
|
||||
|
||||
## 10. Documentation Expectations
|
||||
|
||||
- Update `docs/content/` when user-facing behavior changes.
|
||||
- Update developer docs when architecture, build flow, config schema, or contribution workflow changes.
|
||||
- Keep README changes brief and high-level.
|
||||
- Put detailed user and developer documentation under `docs/`.
|
||||
- Keep `SPEC.md` aligned with build/runtime contract changes.
|
||||
|
||||
## 11. Communication Style
|
||||
|
||||
- Be direct and concise. Start with the answer or action.
|
||||
- No flattery, filler, ceremonial closings, or fake certainty.
|
||||
- Use bullets only when they improve scanning.
|
||||
- Report what changed, how it was verified, and anything not done.
|
||||
- If the user asks for a review, lead with findings and file/line references.
|
||||
|
||||
## 12. When To Ask
|
||||
|
||||
Ask before proceeding when:
|
||||
|
||||
- The request has two plausible interpretations and the choice materially changes behavior or files touched.
|
||||
- The change affects release generation, generated artifacts, migrations, or high-risk Windows behavior in a way the user did not specify.
|
||||
- You need credentials, secrets, production resources, or access you do not have.
|
||||
- The user's stated goal conflicts with the literal request.
|
||||
|
||||
Proceed without asking when:
|
||||
|
||||
- The task is trivial and reversible.
|
||||
- Ambiguity can be resolved by reading the code or running a local command.
|
||||
- The user already answered the question in this session.
|
||||
|
||||
## 13. Project Learnings
|
||||
|
||||
When the user corrects an agent approach, add or tighten one concrete rule here before ending the session. Keep this section short and prune rules that no longer matter.
|
||||
|
||||
- Keep `winutil.ps1` generated-only: change source files, compile to verify, and never stage the generated script.
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# SPEC.md
|
||||
|
||||
## Project Contract
|
||||
|
||||
WinUtil is a Windows PowerShell utility with a WPF interface. The repository is maintained as modular source files, but the released artifact is a single generated `winutil.ps1` script.
|
||||
|
||||
The compiled `winutil.ps1` is not source code for editing or review. It is generated by `Compile.ps1` and produced during release automation. All durable changes must be made to the source files that feed the compiler.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide a single-script Windows utility that can be launched from PowerShell.
|
||||
- Keep development modular enough for contributors to work on functions, config, UI, docs, and tooling independently.
|
||||
- Make install, tweak, feature, repair, update, and ISO workflows discoverable from the WPF UI.
|
||||
- Keep common lists and options declarative in JSON config where possible.
|
||||
- Preserve a repeatable compile process so local builds and GitHub Actions builds produce the distributable script from the same inputs.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- `winutil.ps1` is not hand-maintained.
|
||||
- The project is not structured as a PowerShell module at runtime.
|
||||
- The GUI is not a separate packaged desktop application in this repository's normal release path.
|
||||
- Generated files should not be reviewed as source changes.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
- `Compile.ps1`: build script that creates `winutil.ps1`.
|
||||
- `scripts/start.ps1`: startup/bootstrap segment used at the beginning of the compiled script.
|
||||
- `scripts/main.ps1`: main entrypoint appended at the end of the compiled script.
|
||||
- `functions/public/`: public/UI-facing PowerShell functions.
|
||||
- `functions/private/`: internal helper PowerShell functions.
|
||||
- `config/`: JSON configuration consumed at compile time and embedded into `$sync.configs`.
|
||||
- `xaml/inputXML.xaml`: WPF UI markup embedded into the compiled script.
|
||||
- `tools/autounattend.xml`: unattended setup XML embedded for Windows ISO workflows.
|
||||
- `pester/`: Pester tests for config and function checks.
|
||||
- `lint/PSScriptAnalyser.ps1`: PowerShell Script Analyzer settings.
|
||||
- `docs/`: Hugo documentation site.
|
||||
- `winutil.ps1`: ignored generated build artifact.
|
||||
|
||||
## Compile Specification
|
||||
|
||||
`Compile.ps1` must produce a standalone root `winutil.ps1` by combining all required project files.
|
||||
|
||||
The compile flow is:
|
||||
|
||||
1. Initialize shared state with `$sync = [Hashtable]::Synchronized(@{})` and `$sync.configs = @{}`.
|
||||
2. Read `scripts/start.ps1`.
|
||||
3. Replace `#{replaceme}` in startup code with the current `yy.MM.dd` build date.
|
||||
4. Append raw content from all files under `functions/` recursively.
|
||||
5. For every file in `config/`, parse JSON and embed it into `$sync.configs.<basename>`.
|
||||
6. Special-case `config/applications.json` so application keys are emitted with the `WPFInstall` prefix.
|
||||
7. Embed `xaml/inputXML.xaml` as `$inputXML`.
|
||||
8. Embed `tools/autounattend.xml` as `$WinUtilAutounattendXml`.
|
||||
9. Append `scripts/main.ps1`.
|
||||
10. Write the combined script to `winutil.ps1`.
|
||||
11. If `-Run` is supplied, execute the generated script.
|
||||
|
||||
The generated script must have everything it needs from repository sources embedded or appended by this process.
|
||||
|
||||
## Runtime Model
|
||||
|
||||
- WinUtil runs in PowerShell on Windows and uses WPF for the UI.
|
||||
- Shared mutable state is stored in `$sync`, including configs, UI element references, runspace state, selections, and progress.
|
||||
- Long-running operations should use runspaces or existing async patterns so the UI remains responsive.
|
||||
- UI updates from background work must be dispatched back to the WPF UI thread.
|
||||
- Declarative features such as apps, tweaks, presets, DNS providers, and navigation should stay in `config/*.json` unless code is required.
|
||||
|
||||
## UI And Event Contract
|
||||
|
||||
- UI layout lives in `xaml/inputXML.xaml`.
|
||||
- Named WPF controls are discovered and stored in `$sync`.
|
||||
- Button/action wiring follows existing naming conventions, where an element named like `WPFThingButton` maps to a function named like `Invoke-WPFThingButton`.
|
||||
- When adding controls, ensure the XAML name, config key, and PowerShell function names line up with the existing event system.
|
||||
|
||||
## Configuration Contract
|
||||
|
||||
Config files must remain valid JSON and compile cleanly through `ConvertFrom-Json`.
|
||||
|
||||
`config/applications.json` defines installable applications. Each application entry should include the fields expected by tests and UI code, such as package manager IDs, category, display content, description, and link.
|
||||
|
||||
`config/tweaks.json` defines Windows tweaks. Registry and service changes should include original values or original states when applicable so undo workflows can restore user systems.
|
||||
|
||||
Preset and navigation files should reference valid config keys. Avoid renaming config keys unless all presets, UI references, docs, and code paths are updated together.
|
||||
|
||||
## Safety Requirements
|
||||
|
||||
- Registry, service, package manager, Windows Update, AppX removal, and ISO operations can affect the host system. Changes must be explicit, reversible where practical, and consistent with existing logging and confirmation patterns.
|
||||
- Tweak changes should include undo metadata when the schema supports it.
|
||||
- Package installation should prefer existing WinGet and Chocolatey helper functions.
|
||||
- ISO workflows must not modify the user's original ISO file; they should work on copied/mounted content following existing patterns.
|
||||
|
||||
## Testing And CI
|
||||
|
||||
Expected validation for source changes:
|
||||
|
||||
- `.\Compile.ps1` verifies the compiler can generate `winutil.ps1`.
|
||||
- `.\Compile.ps1 -Run` compiles and launches the generated utility for manual GUI verification.
|
||||
- `Invoke-Pester -Path 'pester/*.Tests.ps1' -Output Detailed` runs the Pester suite.
|
||||
- GitHub Actions also runs a compile check and PowerShell Script Analyzer with `lint/PSScriptAnalyser.ps1`.
|
||||
|
||||
The generated `winutil.ps1` may appear locally after compile. It remains ignored build output and must not be committed.
|
||||
|
||||
## Release Artifact
|
||||
|
||||
GitHub Actions is responsible for producing the release `winutil.ps1` from repository sources. A release should be considered valid only if the generated script came from the compile process, not from direct manual edits to `winutil.ps1`.
|
||||
|
||||
@@ -161,6 +161,15 @@
|
||||
"winget": "Cemu.Cemu",
|
||||
"foss": true
|
||||
},
|
||||
"chatgpt": {
|
||||
"category": "Development",
|
||||
"choco": "na",
|
||||
"content": "ChatGPT Desktop",
|
||||
"description": "The official ChatGPT desktop app for Windows, distributed through the Microsoft Store.",
|
||||
"link": "https://apps.microsoft.com/detail/9nt1r1c2hh7j",
|
||||
"winget": "msstore:9NT1R1C2HH7J",
|
||||
"foss": false
|
||||
},
|
||||
"chatterino": {
|
||||
"category": "Communications",
|
||||
"choco": "chatterino",
|
||||
@@ -188,6 +197,33 @@
|
||||
"winget": "Hibbiki.Chromium",
|
||||
"foss": true
|
||||
},
|
||||
"cinebenchr23": {
|
||||
"category": "Pro Tools",
|
||||
"choco": "na",
|
||||
"content": "Cinebench R23",
|
||||
"description": "Cinebench R23 is a benchmark tool for comparing CPU rendering performance across systems.",
|
||||
"link": "https://www.maxon.net/en/cinebench",
|
||||
"winget": "Maxon.CinebenchR23",
|
||||
"foss": false
|
||||
},
|
||||
"claude": {
|
||||
"category": "Development",
|
||||
"choco": "claude",
|
||||
"content": "Claude Desktop",
|
||||
"description": "Anthropic's Claude desktop application for focused AI-assisted work and chat.",
|
||||
"link": "https://claude.ai/download",
|
||||
"winget": "Anthropic.Claude",
|
||||
"foss": false
|
||||
},
|
||||
"claude-code": {
|
||||
"category": "Development",
|
||||
"choco": "claude-code",
|
||||
"content": "Claude Code",
|
||||
"description": "Anthropic's agentic coding tool for terminal and IDE development workflows.",
|
||||
"link": "https://code.claude.com/",
|
||||
"winget": "Anthropic.ClaudeCode",
|
||||
"foss": false
|
||||
},
|
||||
"cmake": {
|
||||
"category": "Development",
|
||||
"choco": "cmake",
|
||||
@@ -197,6 +233,15 @@
|
||||
"winget": "Kitware.CMake",
|
||||
"foss": true
|
||||
},
|
||||
"codex": {
|
||||
"category": "Development",
|
||||
"choco": "codex",
|
||||
"content": "Codex",
|
||||
"description": "Codex CLI is an OpenAI coding agent that runs locally in your terminal.",
|
||||
"link": "https://developers.openai.com/codex/cli",
|
||||
"winget": "OpenAI.Codex",
|
||||
"foss": true
|
||||
},
|
||||
"cpuz": {
|
||||
"category": "Pro Tools",
|
||||
"choco": "cpu-z",
|
||||
@@ -314,6 +359,15 @@
|
||||
"winget": "Microsoft.DotNet.DesktopRuntime.10",
|
||||
"foss": true
|
||||
},
|
||||
"dropbox": {
|
||||
"category": "Utilities",
|
||||
"choco": "dropbox",
|
||||
"content": "Dropbox",
|
||||
"description": "Dropbox is a cloud storage client for syncing files, sharing content, and keeping documents available across devices.",
|
||||
"link": "https://www.dropbox.com/desktop",
|
||||
"winget": "Dropbox.Dropbox",
|
||||
"foss": false
|
||||
},
|
||||
"eaapp": {
|
||||
"category": "Games",
|
||||
"choco": "ea-app",
|
||||
@@ -539,6 +593,15 @@
|
||||
"winget": "DuongDieuPhap.ImageGlass",
|
||||
"foss": true
|
||||
},
|
||||
"internetdownloadmanager": {
|
||||
"category": "Utilities",
|
||||
"choco": "internet-download-manager",
|
||||
"content": "Internet Download Manager",
|
||||
"description": "Internet Download Manager is a download manager for accelerating, resuming, and scheduling file downloads.",
|
||||
"link": "https://www.internetdownloadmanager.com/",
|
||||
"winget": "Tonec.InternetDownloadManager",
|
||||
"foss": false
|
||||
},
|
||||
"irfanview": {
|
||||
"category": "Multimedia Tools",
|
||||
"choco": "irfanview",
|
||||
@@ -1285,6 +1348,15 @@
|
||||
"winget": "SlackTechnologies.Slack",
|
||||
"foss": false
|
||||
},
|
||||
"startallback": {
|
||||
"category": "Utilities",
|
||||
"choco": "StartAllBack",
|
||||
"content": "StartAllBack",
|
||||
"description": "StartAllBack restores and improves Windows taskbar, Start menu, File Explorer, and shell UI behavior.",
|
||||
"link": "https://www.startallback.com/",
|
||||
"winget": "StartIsBack.StartAllBack",
|
||||
"foss": false
|
||||
},
|
||||
"steam": {
|
||||
"category": "Games",
|
||||
"choco": "steam-client",
|
||||
@@ -1573,6 +1645,15 @@
|
||||
"winget": "Waterfox.Waterfox",
|
||||
"foss": true
|
||||
},
|
||||
"whatsapp": {
|
||||
"category": "Communications",
|
||||
"choco": "na",
|
||||
"content": "WhatsApp Desktop",
|
||||
"description": "WhatsApp Desktop is the official Windows desktop messaging app from Meta, distributed through the Microsoft Store.",
|
||||
"link": "https://apps.microsoft.com/detail/9nksqgp7f2nh",
|
||||
"winget": "msstore:9NKSQGP7F2NH",
|
||||
"foss": false
|
||||
},
|
||||
"wingetui": {
|
||||
"category": "Utilities",
|
||||
"choco": "wingetui",
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"WPFAppxMicrosoft_WindowsFeedbackHub": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Feedback Hub",
|
||||
"Description": "Allows users to submit bug reports, feature suggestions, and diagnostic data directly to Microsoft.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsFeedbackHub"
|
||||
},
|
||||
"WPFAppxMicrosoft_GetHelp": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Get Help",
|
||||
"Description": "Provides access to automated troubleshooting guides, support documentation, and direct Microsoft customer assistance.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.GetHelp"
|
||||
},
|
||||
"WPFAppxMicrosoft_OutlookForWindows": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Outlook for Windows",
|
||||
"Description": "Provides modern email management, calendar scheduling, and contact organization features.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.OutlookForWindows"
|
||||
},
|
||||
"WPFAppxMSTeams": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Microsoft Teams",
|
||||
"Description": "Facilitates instant messaging, video conferencing, file sharing, and workspace collaboration.",
|
||||
"Panel": "0",
|
||||
"PackageId": "MSTeams"
|
||||
},
|
||||
"WPFAppxClipchamp_Clipchamp": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Clipchamp",
|
||||
"Description": "Provides a user-friendly video editor with built-in templates, effects, and timeline editing tools.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Clipchamp.Clipchamp"
|
||||
},
|
||||
"WPFAppxMicrosoft_MicrosoftOfficeHub": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Microsoft 365",
|
||||
"Description": "Serves as a centralized launcher and dashboard for accessing cloud-based Microsoft 365 apps and recent documents.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.MicrosoftOfficeHub"
|
||||
},
|
||||
"WPFAppxMicrosoft_ZuneMusic": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Media Player",
|
||||
"Description": "Plays local audio and video files with modern playlist management and casting capabilities.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.ZuneMusic"
|
||||
},
|
||||
"WPFAppxMicrosoft_BingSearch": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Bing Search",
|
||||
"Description": "Integrates Microsoft Bing search capabilities and web services directly into the operating system.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.BingSearch"
|
||||
},
|
||||
"WPFAppxMicrosoftCorporationII_QuickAssist": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Quick Assist",
|
||||
"Description": "Enables secure remote technical support and screen sharing over an internet connection.",
|
||||
"Panel": "0",
|
||||
"PackageId": "MicrosoftCorporationII.QuickAssist"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsDevHome": {
|
||||
"Category": "Developer Tools",
|
||||
"Content": "Dev Home",
|
||||
"Description": "Provides a specialized dashboard for software developer environment setups, repository syncing, and hardware widgets.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.Windows.DevHome"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsCrossDevice": {
|
||||
"Category": "Microsoft Ecosystem",
|
||||
"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.",
|
||||
"Panel": "0",
|
||||
"PackageId": "MicrosoftWindows.CrossDevice"
|
||||
},
|
||||
"WPFAppxMicrosoft_Todos": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "To Do",
|
||||
"Description": "Creates, tracks, and synchronizes personal tasks, smart lists, and daily reminders.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.Todos"
|
||||
},
|
||||
"WPFAppxMicrosoft_PowerAutomateDesktop": {
|
||||
"Category": "Developer Tools",
|
||||
"Content": "Power Automate",
|
||||
"Description": "Automates repetitive workflows and desktop tasks using low-code visual scripting.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.PowerAutomateDesktop"
|
||||
},
|
||||
"WPFAppxMicrosoft_YourPhone": {
|
||||
"Category": "Microsoft Ecosystem",
|
||||
"Content": "Phone Link",
|
||||
"Description": "Synchronizes text messages, phone notifications, photos, and calls from a mobile device to the desktop.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.YourPhone"
|
||||
},
|
||||
"WPFAppxMicrosoft_MicrosoftStickyNotes": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Sticky Notes",
|
||||
"Description": "Creates quick, floating text notes on the desktop that automatically sync across devices.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.MicrosoftStickyNotes"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsSoundRecorder": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Sound Recorder",
|
||||
"Description": "Records and trims live audio inputs with simple microphone adjustment controls.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsSoundRecorder"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsAlarms": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Clock",
|
||||
"Description": "Features world clocks, alarms, countdown timers, stopwatches, and dedicated focus session tracking.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsAlarms"
|
||||
},
|
||||
"WPFAppxMicrosoft_Paint": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Paint",
|
||||
"Description": "Provides built-in digital sketching, basic image editing, and pixel-level graphic manipulation tools.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.Paint"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsNotepad": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Notepad",
|
||||
"Description": "Provides a lightweight text editor with multi-tab support for plain text files and code snippets.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsNotepad"
|
||||
},
|
||||
"WPFAppxMicrosoft_ScreenSketch": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Snipping Tool",
|
||||
"Description": "Captures screenshots or screen recordings with built-in markup, image cropping, and optical character recognition (OCR).",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.ScreenSketch"
|
||||
},
|
||||
"WPFAppxMicrosoft_Copilot": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Copilot",
|
||||
"Description": "Launches the Microsoft AI companion for contextual answers, creative writing assistance, and intelligent web search.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.Copilot"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsCalculator": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Calculator",
|
||||
"Description": "Performs standard arithmetic, scientific operations, programming calculations, and unit conversions.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsCalculator"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsCamera": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Camera",
|
||||
"Description": "Captures photographs and records video files via connected webcams or imaging hardware.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsCamera"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsPhotos": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Photos",
|
||||
"Description": "Organizes, views, and crops local images with basic color adjustment and album creation tools.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.Windows.Photos"
|
||||
},
|
||||
"WPFAppxMicrosoft_BingNews": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "News",
|
||||
"Description": "Aggregates breaking news headlines, personalized article feeds, and world current events.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.BingNews"
|
||||
},
|
||||
"WPFAppxMicrosoft_BingWeather": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Weather",
|
||||
"Description": "Displays local real-time weather tracking, radar maps, and historical meteorological forecasts.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.BingWeather"
|
||||
},
|
||||
"WPFAppxMicrosoft_GamingApp": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox App",
|
||||
"Description": "Serves as the primary gaming library manager, social community interface, and PC Game Pass dashboard.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.GamingApp"
|
||||
},
|
||||
"WPFAppxMicrosoft_XboxGamingOverlay": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox Game Bar",
|
||||
"Description": "Provides customizable in-game status widgets, audio balancing sliders, system monitoring tools, and gameplay recording.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.XboxGamingOverlay"
|
||||
},
|
||||
"WPFAppxMicrosoft_XboxIdentityProvider": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"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.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.XboxIdentityProvider"
|
||||
},
|
||||
"WPFAppxMicrosoft_XboxSpeechToTextOverlay": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox Speech To Text Overlay",
|
||||
"Description": "Provides system-level live accessibility captions and voice-to-text translation for gaming chat networks.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.XboxSpeechToTextOverlay"
|
||||
},
|
||||
"WPFAppxMicrosoft_Xbox_TCUI": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox TCUI",
|
||||
"Description": "Provides core account connection UI modules for single sign-on flows within game titles. Warning: removing this may break Microsoft account authentication in games and apps that do not otherwise require the Xbox app.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.Xbox.TCUI"
|
||||
},
|
||||
"WPFAppxMicrosoft_StartExperiencesApp": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Start Experiences App",
|
||||
"Description": "Powers the Windows Widgets board, delivering a personalized feed of news, weather, sports, and finance content.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.StartExperiencesApp"
|
||||
},
|
||||
"WPFAppxMicrosoft_MicrosoftSolitaireCollection": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Solitaire Collection",
|
||||
"Description": "Bundles built-in card game modes including Klondike, Spider, FreeCell, Pyramid, and TriPeaks alongside daily challenges.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.MicrosoftSolitaireCollection"
|
||||
}
|
||||
}
|
||||
+21
-3
@@ -4,7 +4,6 @@
|
||||
"WPFTweaksConsumerFeatures",
|
||||
"WPFTweaksDisableExplorerAutoDiscovery",
|
||||
"WPFTweaksWPBT",
|
||||
"WPFTweaksDeBloat",
|
||||
"WPFTweaksLocation",
|
||||
"WPFTweaksServices",
|
||||
"WPFTweaksTelemetry",
|
||||
@@ -16,17 +15,16 @@
|
||||
],
|
||||
"Minimal": [
|
||||
"WPFTweaksConsumerFeatures",
|
||||
"WPFTweaksDeBloat",
|
||||
"WPFTweaksWPBT",
|
||||
"WPFTweaksServices",
|
||||
"WPFTweaksTelemetry"
|
||||
],
|
||||
"Advanced": [
|
||||
"WPFTweaksRestorePoint",
|
||||
"WPFTweaksActivity",
|
||||
"WPFTweaksConsumerFeatures",
|
||||
"WPFTweaksDisableExplorerAutoDiscovery",
|
||||
"WPFTweaksWPBT",
|
||||
"WPFTweaksDeBloat",
|
||||
"WPFTweaksLocation",
|
||||
"WPFTweaksServices",
|
||||
"WPFTweaksTelemetry",
|
||||
@@ -39,5 +37,25 @@
|
||||
"WPFTweaksRemoveOneDrive",
|
||||
"WPFTweaksWindowsAI",
|
||||
"WPFTweaksRightClickMenu"
|
||||
],
|
||||
"AppxDefault": [
|
||||
"WPFAppxMicrosoft_WindowsFeedbackHub",
|
||||
"WPFAppxMicrosoft_GetHelp",
|
||||
"WPFAppxMicrosoft_MicrosoftOfficeHub",
|
||||
"WPFAppxMicrosoft_WindowsCalculator",
|
||||
"WPFAppxClipchamp_Clipchamp",
|
||||
"WPFAppxMicrosoft_WindowsAlarms",
|
||||
"WPFAppxMicrosoftCorporationII_QuickAssist",
|
||||
"WPFAppxMicrosoft_WindowsSoundRecorder",
|
||||
"WPFAppxMicrosoft_MicrosoftStickyNotes",
|
||||
"WPFAppxMicrosoft_Todos",
|
||||
"WPFAppxMicrosoft_MicrosoftSolitaireCollection",
|
||||
"WPFAppxMicrosoft_PowerAutomateDesktop",
|
||||
"WPFAppxMicrosoft_WindowsDevHome",
|
||||
"WPFAppxMicrosoft_BingWeather",
|
||||
"WPFAppxMicrosoft_StartExperiencesApp",
|
||||
"WPFAppxMicrosoft_BingNews",
|
||||
"WPFAppxMicrosoft_Copilot",
|
||||
"WPFAppxMicrosoft_BingSearch"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -65,11 +65,13 @@
|
||||
"ButtonConfigBackgroundColor": "#F7F7F7",
|
||||
"ButtonUpdatesBackgroundColor": "#F7F7F7",
|
||||
"ButtonWin11ISOBackgroundColor": "#F7F7F7",
|
||||
"ButtonAppxBackgroundColor": "#F7F7F7",
|
||||
"ButtonInstallForegroundColor": "#232629",
|
||||
"ButtonTweaksForegroundColor": "#232629",
|
||||
"ButtonConfigForegroundColor": "#232629",
|
||||
"ButtonUpdatesForegroundColor": "#232629",
|
||||
"ButtonWin11ISOForegroundColor": "#232629",
|
||||
"ButtonAppxForegroundColor": "#232629",
|
||||
"ButtonBackgroundColor": "#F5F5F5",
|
||||
"ButtonBackgroundPressedColor": "#1A1A1A",
|
||||
"ButtonBackgroundMouseoverColor": "#C2C2C2",
|
||||
@@ -105,11 +107,13 @@
|
||||
"ButtonConfigBackgroundColor": "#444444",
|
||||
"ButtonUpdatesBackgroundColor": "#555555",
|
||||
"ButtonWin11ISOBackgroundColor": "#666666",
|
||||
"ButtonAppxBackgroundColor": "#777777",
|
||||
"ButtonInstallForegroundColor": "#F7F7F7",
|
||||
"ButtonTweaksForegroundColor": "#F7F7F7",
|
||||
"ButtonConfigForegroundColor": "#F7F7F7",
|
||||
"ButtonUpdatesForegroundColor": "#F7F7F7",
|
||||
"ButtonWin11ISOForegroundColor": "#F7F7F7",
|
||||
"ButtonAppxForegroundColor": "#F7F7F7",
|
||||
"ButtonBackgroundColor": "#1E3747",
|
||||
"ButtonBackgroundPressedColor": "#F7F7F7",
|
||||
"ButtonBackgroundMouseoverColor": "#3B4252",
|
||||
|
||||
+22
-74
@@ -91,8 +91,7 @@
|
||||
"OriginalValue": "<RemoveEntry>"
|
||||
}
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/revertstartmenu",
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/revertstartmenu"
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/revertstartmenu"
|
||||
},
|
||||
"WPFTweaksDisableStoreSearch": {
|
||||
"Content": "Microsoft Store Recommended Search Results - Disable",
|
||||
@@ -587,15 +586,25 @@
|
||||
},
|
||||
"WPFTweaksRemoveEdge": {
|
||||
"Content": "Microsoft Edge - Remove",
|
||||
"Description": "Unblocks Microsoft Edge uninstaller restrictions then uses that uninstaller to remove Microsoft Edge.",
|
||||
"Description": "Uninstalls Microsoft Edge by creating dummy MicrosoftEdge.exe file in the legacy Edge folder. This tricks Windows into unlocking the official Edge uninstaller allowing for a system-level removal.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"InvokeScript": [
|
||||
"Invoke-WinUtilRemoveEdge"
|
||||
"
|
||||
$Path = Resolve-Path -Path \"$Env:ProgramFiles (x86)\\Microsoft\\Edge\\Application\\*\\Installer\\setup.exe\" | Select-Object -Last 1
|
||||
|
||||
if (Test-Path $Path) {
|
||||
New-Item -Path \"$Env:SystemRoot\\SystemApps\\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\\MicrosoftEdge.exe\" -Force
|
||||
Start-Process -FilePath $Path -ArgumentList \"--uninstall --system-level --force-uninstall --delete-profile\" -Wait
|
||||
Write-Host \"Microsoft Edge was removed\"
|
||||
} else {
|
||||
Write-Host \"Microsoft Edge is not installed\"
|
||||
}
|
||||
"
|
||||
],
|
||||
"UndoScript": [
|
||||
"
|
||||
Write-Host 'Installing Microsoft Edge...'
|
||||
Write-Host \"Installing Microsoft Edge...\"
|
||||
winget install Microsoft.Edge --source winget
|
||||
"
|
||||
],
|
||||
@@ -641,7 +650,7 @@
|
||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||
|
||||
Write-Host \"Uninstalling OneDrive...\"
|
||||
Start-Process 'C:\\Windows\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
||||
Start-Process '$Env:SystemRoot\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
||||
|
||||
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
||||
Write-Host \"Removing leftover OneDrive Files...\"
|
||||
@@ -649,7 +658,7 @@
|
||||
Stop-Process -Name FileCoAuth,Explorer
|
||||
|
||||
Remove-Item \"$Env:LocalAppData\\Microsoft\\OneDrive\" -Recurse -Force
|
||||
Remove-Item \"C:\\ProgramData\\Microsoft OneDrive\" -Recurse -Force
|
||||
Remove-Item \"$Env:ProgramData\\Microsoft OneDrive\" -Recurse -Force
|
||||
|
||||
# Grant back permission to access OneDrive folder
|
||||
icacls $Env:OneDrive /grant \"Administrators:(D,DC)\"
|
||||
@@ -803,70 +812,6 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/z--advanced-tweaks---caution/display"
|
||||
},
|
||||
"WPFTweaksXboxRemoval": {
|
||||
"Content": "Xbox & Gaming Components - Remove",
|
||||
"Description": "Removes Xbox services, the Xbox app, Game Bar, and related authentication components.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\GameDVR",
|
||||
"Name": "AppCaptureEnabled",
|
||||
"Value": "0",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "1"
|
||||
}
|
||||
],
|
||||
"appx": [
|
||||
"Microsoft.XboxIdentityProvider",
|
||||
"Microsoft.XboxSpeechToTextOverlay",
|
||||
"Microsoft.GamingApp",
|
||||
"Microsoft.Xbox.TCUI",
|
||||
"Microsoft.XboxGamingOverlay"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/z--advanced-tweaks---caution/xboxremoval"
|
||||
},
|
||||
"WPFTweaksDeBloat": {
|
||||
"Content": "Unwanted Pre-Installed Apps - Remove",
|
||||
"Description": "This will remove a bunch of Windows pre-installed applications which most people dont want on their system.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"appx": [
|
||||
"Microsoft.WindowsFeedbackHub",
|
||||
"Microsoft.BingNews",
|
||||
"Microsoft.BingSearch",
|
||||
"Microsoft.BingWeather",
|
||||
"Clipchamp.Clipchamp",
|
||||
"Microsoft.Todos",
|
||||
"Microsoft.PowerAutomateDesktop",
|
||||
"Microsoft.MicrosoftSolitaireCollection",
|
||||
"Microsoft.WindowsSoundRecorder",
|
||||
"Microsoft.MicrosoftStickyNotes",
|
||||
"Microsoft.Windows.DevHome",
|
||||
"Microsoft.Paint",
|
||||
"Microsoft.OutlookForWindows",
|
||||
"Microsoft.WindowsAlarms",
|
||||
"Microsoft.StartExperiencesApp",
|
||||
"Microsoft.GetHelp",
|
||||
"Microsoft.ZuneMusic",
|
||||
"MicrosoftCorporationII.QuickAssist",
|
||||
"MSTeams"
|
||||
],
|
||||
"InvokeScript": [
|
||||
"
|
||||
$TeamsPath = \"$Env:LocalAppData\\Microsoft\\Teams\\Update.exe\"
|
||||
|
||||
if (Test-Path $TeamsPath) {
|
||||
Write-Host \"Uninstalling Teams\"
|
||||
Start-Process $TeamsPath -ArgumentList -uninstall -wait
|
||||
|
||||
Write-Host \"Deleting Teams directory\"
|
||||
Remove-Item $TeamsPath -Recurse -Force
|
||||
}
|
||||
"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/debloat"
|
||||
},
|
||||
"WPFTweaksReservedStorage": {
|
||||
"Content": "Disable Reserved Storage",
|
||||
"Description": "Disables Windows Reserved Storage (7-10 GB held for updates/temp files). Recommended only on small drives. Re-enable before major Windows feature updates to avoid installation failures.",
|
||||
@@ -969,7 +914,10 @@
|
||||
|
||||
Get-AppxPackage -AllUsers \"*Copilot*\" | Remove-AppxPackage -AllUsers
|
||||
Get-AppxPackage -AllUsers Microsoft.MicrosoftOfficeHub | Remove-AppxPackage -AllUsers
|
||||
Remove-AppxPackage $Appx
|
||||
|
||||
if ($Appx) {
|
||||
Remove-AppxPackage $Appx
|
||||
}
|
||||
|
||||
Set-Service -Name WSAIFabricSvc -StartupType Disabled
|
||||
Disable-WindowsOptionalFeature -FeatureName Recall -Online -NoRestart
|
||||
@@ -1018,7 +966,7 @@
|
||||
],
|
||||
"InvokeScript": [
|
||||
"
|
||||
$RazerPath = \"C:\\Windows\\Installer\\Razer\"
|
||||
$RazerPath = \"$Env:SystemRoot\\Installer\\Razer\"
|
||||
|
||||
if (Test-Path $RazerPath) {
|
||||
Remove-Item $RazerPath\\* -Recurse -Force
|
||||
@@ -1031,7 +979,7 @@
|
||||
],
|
||||
"UndoScript": [
|
||||
"
|
||||
icacls \"C:\\Windows\\Installer\\Razer\" /remove:d Everyone
|
||||
icacls \"$Env:SystemRoot\\Installer\\Razer\" /remove:d Everyone
|
||||
"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/z--advanced-tweaks---caution/razerblock"
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "System Tray Battery Percentage"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1303}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1251}
|
||||
"WPFToggleBatteryPercentage": {
|
||||
"Content": "System Tray Battery Percentage",
|
||||
"Description": "Shows numeric battery percentage next to the battery icon in the system tray.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Start Menu Bing Search"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1638}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1586}
|
||||
"WPFToggleBingSearch": {
|
||||
"Content": "Start Menu Bing Search",
|
||||
"Description": "Toggles Bing web search results in Windows Search.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Dark Theme for Windows"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1321}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1269}
|
||||
"WPFToggleDarkMode": {
|
||||
"Content": "Dark Theme for Windows",
|
||||
"Description": "Dark Mode for the system and applications.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "BSoD Verbose Mode"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1277}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1225}
|
||||
"WPFToggleDetailedBSoD": {
|
||||
"Content": "BSoD Verbose Mode",
|
||||
"Description": "Gives more information when you blue screen.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Lock Screen - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1674}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1622}
|
||||
"WPFTweaksDisableLockscreen": {
|
||||
"Content": "Lock Screen - Disable",
|
||||
"Description": "Skips the lock screen entirely and goes directly to the sign-in screen on boot and wake.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Game Mode"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1817}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1765}
|
||||
"WPFToggleGameMode": {
|
||||
"Content": "Game Mode",
|
||||
"Description": "Toggles Windows prioritizes gaming performance by allocating system resources to games.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer Hidden Files"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1391}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1339}
|
||||
"WPFToggleHiddenFiles": {
|
||||
"Content": "File Explorer Hidden Files",
|
||||
"Description": "Reveals hidden files in Explorer.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Settings Home Page"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1620}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1568}
|
||||
"WPFToggleHideSettingsHome": {
|
||||
"Content": "Settings Home Page",
|
||||
"Description": "Toggles the Home Page in the Windows Settings app.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Logon Screen Acrylic Blur"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1656}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1604}
|
||||
"WPFToggleLoginBlur": {
|
||||
"Content": "Logon Screen Acrylic Blur",
|
||||
"Description": "Toggles the acrylic blur effect on login screen background.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Enable Long Paths"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1843}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1791}
|
||||
"WPFToggleLongPaths": {
|
||||
"Content": "Enable Long Paths",
|
||||
"Description": "Toggles support for file paths longer than 260 characters in Explorer.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Mouse Acceleration"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1524}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1472}
|
||||
"WPFToggleMouseAcceleration": {
|
||||
"Content": "Mouse Acceleration",
|
||||
"Description": "Makes it so Cursor movement is affected by the speed of your physical mouse movements.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Multiplane Overlay"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1498}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1446}
|
||||
"WPFToggleMultiplaneOverlay": {
|
||||
"Content": "Multiplane Overlay",
|
||||
"Description": "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Microsoft Outlook New Version"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1437}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1385}
|
||||
"WPFToggleNewOutlook": {
|
||||
"Content": "Microsoft Outlook New Version",
|
||||
"Description": "This will ensures the classic Outlook application is used.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Num Lock on Startup"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1558}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1506}
|
||||
"WPFToggleNumLock": {
|
||||
"Content": "Num Lock on Startup",
|
||||
"Description": "Toggle the Num Lock key state when your computer starts.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "S3 Sleep"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1602}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1550}
|
||||
"WPFToggleS3Sleep": {
|
||||
"Content": "S3 Sleep",
|
||||
"Description": "Toggles between Modern Standby and S3 Sleep, which cuts off power to the CPU while continuing to refresh the memory.",
|
||||
|
||||
@@ -3,6 +3,25 @@ title: "Scrollbars Always Visible"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1427}
|
||||
"WPFToggleScrollbars": {
|
||||
"Content": "Scrollbars Always Visible",
|
||||
"Description": "If enabled, scrollbars will always be visible. If disabled, Windows will automatically hide scrollbars when not in use.",
|
||||
"category": "Customize Preferences",
|
||||
"panel": "2",
|
||||
"Type": "Toggle",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKCU:\\Control Panel\\Accessibility",
|
||||
"Name": "DynamicScrollbars",
|
||||
"Value": "0",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "1",
|
||||
"DefaultState": "false",
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/customize-preferences/scrollbars"
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
## Registry Changes
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer File Extensions"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1363}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1311}
|
||||
"WPFToggleShowExt": {
|
||||
"Content": "File Explorer File Extensions",
|
||||
"Description": "Shows .file extensions in Explorer (.exe, .png, etc.)",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "S0 Sleep Network Connectivity"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1584}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1532}
|
||||
"WPFToggleStandbyFix": {
|
||||
"Content": "S0 Sleep Network Connectivity",
|
||||
"Description": "Toggles network connectivity during S0 Sleep which is low power idle in modern laptops.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Start Menu Recommendations"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1691}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1639}
|
||||
"WPFToggleStartMenuRecommendations": {
|
||||
"Content": "Start Menu Recommendations",
|
||||
"Description": "Toggles the recommendations section in the Start Menu. WARNING: This will also disable Windows Spotlight on your Lock Screen as a side effect.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Sticky Keys"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1735}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1683}
|
||||
"WPFToggleStickyKeys": {
|
||||
"Content": "Sticky Keys",
|
||||
"Description": "Toggles the Sticky Keys, which activate when clicking shift rapidly.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Taskbar Task View Icon"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1799}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1747}
|
||||
"WPFToggleTaskView": {
|
||||
"Content": "Taskbar Task View Icon",
|
||||
"Description": "Toggles the Task View Button in the Taskbar.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Taskbar Centered Icons"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1753}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1701}
|
||||
"WPFToggleTaskbarAlignment": {
|
||||
"Content": "Taskbar Centered Icons",
|
||||
"Description": "Toggles the Taskbar alignment either to the left or center.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Taskbar Search Icon"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1781}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1729}
|
||||
"WPFToggleTaskbarSearch": {
|
||||
"Content": "Taskbar Search Icon",
|
||||
"Description": "Toggles the Search Button on the Taskbar.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Logon Verbose Mode"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1419}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1367}
|
||||
"WPFToggleVerboseLogon": {
|
||||
"Content": "Logon Verbose Mode",
|
||||
"Description": "Show detailed messages during startup/shutdown.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "ConsumerFeatures - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=431}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=430}
|
||||
"WPFTweaksConsumerFeatures": {
|
||||
"Content": "ConsumerFeatures - Disable",
|
||||
"Description": "Windows will not automatically install any games, third-party apps, or application links from the Windows Store for the signed-in user. Some default Apps will be inaccessible (e.g. Phone Link).",
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
title: "Unwanted Pre-Installed Apps - Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=829}
|
||||
"WPFTweaksDeBloat": {
|
||||
"Content": "Unwanted Pre-Installed Apps - Remove",
|
||||
"Description": "This will remove a bunch of Windows pre-installed applications which most people dont want on their system.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"appx": [
|
||||
"Microsoft.WindowsFeedbackHub",
|
||||
"Microsoft.BingNews",
|
||||
"Microsoft.BingSearch",
|
||||
"Microsoft.BingWeather",
|
||||
"Clipchamp.Clipchamp",
|
||||
"Microsoft.Todos",
|
||||
"Microsoft.PowerAutomateDesktop",
|
||||
"Microsoft.MicrosoftSolitaireCollection",
|
||||
"Microsoft.WindowsSoundRecorder",
|
||||
"Microsoft.MicrosoftStickyNotes",
|
||||
"Microsoft.Windows.DevHome",
|
||||
"Microsoft.Paint",
|
||||
"Microsoft.OutlookForWindows",
|
||||
"Microsoft.WindowsAlarms",
|
||||
"Microsoft.StartExperiencesApp",
|
||||
"Microsoft.GetHelp",
|
||||
"Microsoft.ZuneMusic",
|
||||
"MicrosoftCorporationII.QuickAssist",
|
||||
"MSTeams"
|
||||
],
|
||||
"InvokeScript": [
|
||||
"
|
||||
$TeamsPath = \"$Env:LocalAppData\\Microsoft\\Teams\\Update.exe\"
|
||||
|
||||
if (Test-Path $TeamsPath) {
|
||||
Write-Host \"Uninstalling Teams\"
|
||||
Start-Process $TeamsPath -ArgumentList -uninstall -wait
|
||||
|
||||
Write-Host \"Deleting Teams directory\"
|
||||
Remove-Item $TeamsPath -Recurse -Force
|
||||
}
|
||||
"
|
||||
],
|
||||
```
|
||||
@@ -3,7 +3,7 @@ title: "Temporary Files - Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1117}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1065}
|
||||
"WPFTweaksDeleteTempFiles": {
|
||||
"Content": "Temporary Files - Remove",
|
||||
"Description": "Erases TEMP Folders.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Delivery Optimization - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=572}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=571}
|
||||
"WPFTweaksDeliveryOptimization": {
|
||||
"Content": "Delivery Optimization - Disable",
|
||||
"Description": "Stops Windows from using your bandwidth to upload updates to other PCs on the internet or local network.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "BitLocker - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=604}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=613}
|
||||
"WPFTweaksDisableBitLocker": {
|
||||
"Content": "BitLocker - Disable",
|
||||
"Description": "Disables BitLocker.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer Automatic Folder Discovery - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1222}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1170}
|
||||
"WPFTweaksDisableExplorerAutoDiscovery": {
|
||||
"Content": "File Explorer Automatic Folder Discovery - Disable",
|
||||
"Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience. WARNING! Will disable File Explorer grouping.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Microsoft Store Recommended Search Results - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=97}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=96}
|
||||
"WPFTweaksDisableStoreSearch": {
|
||||
"Content": "Microsoft Store Recommended Search Results - Disable",
|
||||
"Description": "Will not display recommended Microsoft Store apps when searching for apps in the Start menu.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Disk Cleanup - Run"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1104}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1052}
|
||||
"WPFTweaksDiskCleanup": {
|
||||
"Content": "Disk Cleanup - Run",
|
||||
"Description": "Runs Disk Cleanup on Drive C: and removes old Windows Updates.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "End Task With Right Click - Enable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=910}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=855}
|
||||
"WPFTweaksEndTaskOnTaskbar": {
|
||||
"Content": "End Task With Right Click - Enable",
|
||||
"Description": "Enables option to end task when right clicking a program in the taskbar.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Location Tracking - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=110}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=109}
|
||||
"WPFTweaksLocation": {
|
||||
"Content": "Location Tracking - Disable",
|
||||
"Description": "Disables Location Tracking.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Restore Point - Create"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=883}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=828}
|
||||
"WPFTweaksRestorePoint": {
|
||||
"Content": "Restore Point - Create",
|
||||
"Description": "Creates a restore point at runtime in case a revert is needed from WinUtil modifications.",
|
||||
|
||||
@@ -3,6 +3,22 @@ title: "Start Menu Previous Layout - Enable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=80}
|
||||
"WPFTweaksRevertStartMenu": {
|
||||
"Content": "Start Menu Previous Layout - Enable",
|
||||
"Description": "Bring back the old Start Menu layout from before the gradual rollout of the new one in 25H2. On newer versions of Windows !!THIS TWEAK WILL NOT WORK!!",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKLM:\\SYSTEM\\ControlSet001\\Control\\FeatureManagement\\Overrides\\8\\3036241548",
|
||||
"Name": "EnabledState",
|
||||
"Value": "1",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "<RemoveEntry>"
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
## Registry Changes
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Services - Set to Manual"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=147}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=146}
|
||||
"WPFTweaksServices": {
|
||||
"Content": "Services - Set to Manual",
|
||||
"Description": "Sets some services to Manual startup and adjusts the SvcHostSplitThresholdInKB registry value to better match system memory, which can significantly reduce the number of svchost.exe processes.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Telemetry - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=447}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=446}
|
||||
"WPFTweaksTelemetry": {
|
||||
"Content": "Telemetry - Disable",
|
||||
"Description": "Disables Microsoft Telemetry.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Windows Platform Binary Table (WPBT) - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=982}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=930}
|
||||
"WPFTweaksWPBT": {
|
||||
"Content": "Windows Platform Binary Table (WPBT) - Disable",
|
||||
"Description": "If enabled, WPBT allows your computer vendor to execute programs at boot time, such as anti-theft software, software drivers, as well as force install software without user consent. Poses potential security risk.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Adobe URL Block List - Enable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1062}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1010}
|
||||
"WPFTweaksBlockAdobeNet": {
|
||||
"Content": "Adobe URL Block List - Enable",
|
||||
"Description": "Reduces user interruptions by selectively blocking connections to Adobe's activation and telemetry servers. Credit: Ruddernation-Designs",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Brave Browser - Debloat"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=187}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=186}
|
||||
"WPFTweaksBraveDebloat": {
|
||||
"Content": "Brave Browser - Debloat",
|
||||
"Description": "Disables various annoyances like Brave Rewards, Leo AI, Crypto Wallet and VPN.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Background Apps - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1190}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1138}
|
||||
"WPFTweaksDisableBGapps": {
|
||||
"Content": "Background Apps - Disable",
|
||||
"Description": "Disables all Microsoft Store apps from running in the background, which has to be done individually since Windows 11.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Fullscreen Optimizations - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1206}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1154}
|
||||
"WPFTweaksDisableFSO": {
|
||||
"Content": "Fullscreen Optimizations - Disable",
|
||||
"Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "IPv6 - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1168}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1116}
|
||||
"WPFTweaksDisableIPv6": {
|
||||
"Content": "IPv6 - Disable",
|
||||
"Description": "Disables IPv6.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "System Tray Notifications & Calendar - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1039}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=987}
|
||||
"WPFTweaksDisableNotifications": {
|
||||
"Content": "System Tray Notifications & Calendar - Disable",
|
||||
"Description": "Disables all Notifications INCLUDING Calendar.",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ title: "RDP Unsigned File Warnings - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=280}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=279}
|
||||
"WPFTweaksDisableWarningForUnsignedRdp": {
|
||||
"Content": "RDP Unsigned File Warnings - Disable",
|
||||
"Description": "Disables warnings shown when launching unsigned RDP files introduced with the latest Windows 10 and 11 updates.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Visual Effects - Set to Best Performance"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=707}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=716}
|
||||
"WPFTweaksDisplay": {
|
||||
"Content": "Visual Effects - Set to Best Performance",
|
||||
"Description": "Sets the system preferences to performance. You can do this manually with sysdm.cpl as well.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Microsoft Edge - Debloat"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=303}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=302}
|
||||
"WPFTweaksEdgeDebloat": {
|
||||
"Content": "Microsoft Edge - Debloat",
|
||||
"Description": "Disables various telemetry options, popups, and other annoyances in Edge.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "IPv6 - Set IPv4 as Preferred"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1130}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1078}
|
||||
"WPFTweaksIPv46": {
|
||||
"Content": "IPv6 - Set IPv4 as Preferred",
|
||||
"Description": "Setting the IPv4 preference can have latency and security benefits on private networks where IPv6 is not configured.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Razer Software Auto-Install - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=998}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=946}
|
||||
"WPFTweaksRazerBlock": {
|
||||
"Content": "Razer Software Auto-Install - Disable",
|
||||
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
|
||||
@@ -27,7 +27,7 @@ description: ""
|
||||
],
|
||||
"InvokeScript": [
|
||||
"
|
||||
$RazerPath = \"C:\\Windows\\Installer\\Razer\"
|
||||
$RazerPath = \"$Env:SystemRoot\\Installer\\Razer\"
|
||||
|
||||
if (Test-Path $RazerPath) {
|
||||
Remove-Item $RazerPath\\* -Recurse -Force
|
||||
@@ -40,7 +40,7 @@ description: ""
|
||||
],
|
||||
"UndoScript": [
|
||||
"
|
||||
icacls \"C:\\Windows\\Installer\\Razer\" /remove:d Everyone
|
||||
icacls \"$Env:SystemRoot\\Installer\\Razer\" /remove:d Everyone
|
||||
"
|
||||
],
|
||||
```
|
||||
|
||||
@@ -3,18 +3,28 @@ title: "Microsoft Edge - Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=588}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=587}
|
||||
"WPFTweaksRemoveEdge": {
|
||||
"Content": "Microsoft Edge - Remove",
|
||||
"Description": "Unblocks Microsoft Edge uninstaller restrictions then uses that uninstaller to remove Microsoft Edge.",
|
||||
"Description": "Uninstalls Microsoft Edge by creating dummy MicrosoftEdge.exe file in the legacy Edge folder. This tricks Windows into unlocking the official Edge uninstaller allowing for a system-level removal.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"InvokeScript": [
|
||||
"Invoke-WinUtilRemoveEdge"
|
||||
"
|
||||
$Path = Resolve-Path -Path \"$Env:ProgramFiles (x86)\\Microsoft\\Edge\\Application\\*\\Installer\\setup.exe\" | Select-Object -Last 1
|
||||
|
||||
if (Test-Path $Path) {
|
||||
New-Item -Path \"$Env:SystemRoot\\SystemApps\\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\\MicrosoftEdge.exe\" -Force
|
||||
Start-Process -FilePath $Path -ArgumentList \"--uninstall --system-level --force-uninstall --delete-profile\" -Wait
|
||||
Write-Host \"Microsoft Edge was removed\"
|
||||
} else {
|
||||
Write-Host \"Microsoft Edge is not installed\"
|
||||
}
|
||||
"
|
||||
],
|
||||
"UndoScript": [
|
||||
"
|
||||
Write-Host 'Installing Microsoft Edge...'
|
||||
Write-Host \"Installing Microsoft Edge...\"
|
||||
winget install Microsoft.Edge --source winget
|
||||
"
|
||||
],
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer Home and Gallery - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=677}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=686}
|
||||
"WPFTweaksRemoveHomeAndGallery": {
|
||||
"Content": "File Explorer Home and Gallery - Disable",
|
||||
"Description": "Removes the Home and Gallery from Explorer and sets This PC as default.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Microsoft OneDrive - Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=633}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=642}
|
||||
"WPFTweaksRemoveOneDrive": {
|
||||
"Content": "Microsoft OneDrive - Remove",
|
||||
"Description": "Denies permission to remove OneDrive user files, then uses its own uninstaller to remove it and restores the original permission afterward.",
|
||||
@@ -15,7 +15,7 @@ description: ""
|
||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||
|
||||
Write-Host \"Uninstalling OneDrive...\"
|
||||
Start-Process 'C:\\Windows\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
||||
Start-Process '$Env:SystemRoot\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
||||
|
||||
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
||||
Write-Host \"Removing leftover OneDrive Files...\"
|
||||
@@ -23,7 +23,7 @@ description: ""
|
||||
Stop-Process -Name FileCoAuth,Explorer
|
||||
|
||||
Remove-Item \"$Env:LocalAppData\\Microsoft\\OneDrive\" -Recurse -Force
|
||||
Remove-Item \"C:\\ProgramData\\Microsoft OneDrive\" -Recurse -Force
|
||||
Remove-Item \"$Env:ProgramData\\Microsoft OneDrive\" -Recurse -Force
|
||||
|
||||
# Grant back permission to access OneDrive folder
|
||||
icacls $Env:OneDrive /grant \"Administrators:(D,DC)\"
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Disable Reserved Storage"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=870}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=815}
|
||||
"WPFTweaksReservedStorage": {
|
||||
"Content": "Disable Reserved Storage",
|
||||
"Description": "Disables Windows Reserved Storage (7-10 GB held for updates/temp files). Recommended only on small drives. Re-enable before major Windows feature updates to avoid installation failures.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Right-Click Menu Previous Layout - Enable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1088}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1036}
|
||||
"WPFTweaksRightClickMenu": {
|
||||
"Content": "Right-Click Menu Previous Layout - Enable",
|
||||
"Description": "Restores the classic context menu when right-clicking in File Explorer, replacing the simplified Windows 11 version.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Storage Sense - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=926}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=871}
|
||||
"WPFTweaksStorage": {
|
||||
"Content": "Storage Sense - Disable",
|
||||
"Description": "Storage Sense deletes temp files automatically.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Teredo - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1146}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1094}
|
||||
"WPFTweaksTeredo": {
|
||||
"Content": "Teredo - Disable",
|
||||
"Description": "Teredo network tunneling is an IPv6 feature that can cause additional latency, but may cause problems with some games.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Date & Time - Set Time to UTC"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=617}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=626}
|
||||
"WPFTweaksUTC": {
|
||||
"Content": "Date & Time - Set Time to UTC",
|
||||
"Description": "Essential for computers that are dual booting. Fixes the time sync with Linux systems.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Windows AI - Disable And Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=942}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=887}
|
||||
"WPFTweaksWindowsAI": {
|
||||
"Content": "Windows AI - Disable And Remove",
|
||||
"Description": "Removes and disables all AI features/packages",
|
||||
@@ -34,7 +34,10 @@ description: ""
|
||||
|
||||
Get-AppxPackage -AllUsers \"*Copilot*\" | Remove-AppxPackage -AllUsers
|
||||
Get-AppxPackage -AllUsers Microsoft.MicrosoftOfficeHub | Remove-AppxPackage -AllUsers
|
||||
Remove-AppxPackage $Appx
|
||||
|
||||
if ($Appx) {
|
||||
Remove-AppxPackage $Appx
|
||||
}
|
||||
|
||||
Set-Service -Name WSAIFabricSvc -StartupType Disabled
|
||||
Disable-WindowsOptionalFeature -FeatureName Recall -Online -NoRestart
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
title: "Xbox & Gaming Components - Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=806}
|
||||
"WPFTweaksXboxRemoval": {
|
||||
"Content": "Xbox & Gaming Components - Remove",
|
||||
"Description": "Removes Xbox services, the Xbox app, Game Bar, and related authentication components.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\GameDVR",
|
||||
"Name": "AppCaptureEnabled",
|
||||
"Value": "0",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "1"
|
||||
}
|
||||
],
|
||||
"appx": [
|
||||
"Microsoft.XboxIdentityProvider",
|
||||
"Microsoft.XboxSpeechToTextOverlay",
|
||||
"Microsoft.GamingApp",
|
||||
"Microsoft.Xbox.TCUI",
|
||||
"Microsoft.XboxGamingOverlay"
|
||||
],
|
||||
```
|
||||
|
||||
## Registry Changes
|
||||
|
||||
Applications and System Components store and retrieve configuration data to modify Windows settings, so we can use the registry to change many settings in one place.
|
||||
|
||||
You can find information about the registry on [Wikipedia](https://en.wikipedia.org/wiki/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).
|
||||
@@ -3,7 +3,7 @@ title: "DNS - Set to:"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1868}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1816}
|
||||
"WPFchangedns": {
|
||||
"Content": "DNS - Set to:",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
|
||||
@@ -51,6 +51,7 @@ Click **Run Windows ISO Modification and Creator** to start the customization pr
|
||||
- **Disable BitLocker and device encryption** — removes startup overhead
|
||||
- **Disable Chat icon** — removes chat taskbar button
|
||||
- **Strip unused editions** — keeps only your selected edition, saving 1–2 GB per removed edition
|
||||
- **Pin the selected edition during setup** — writes setup metadata so OEM firmware keys for a different edition do not force the installer down the wrong product-key path
|
||||
- **Clean the component store** — runs DISM cleanup to reclaim another 300–800 MB
|
||||
|
||||
**Privacy & Telemetry Tweaks:**
|
||||
@@ -132,6 +133,7 @@ When you install Windows 11 from your modified ISO:
|
||||
| USB drive not showing up | Plug it in, wait a few seconds, then click **Refresh** |
|
||||
| Modification seems stuck | The WIM dismount step is slow — wait at least 10 minutes before assuming it's frozen |
|
||||
| "Access Denied" error | Make sure WinUtil is running as Administrator |
|
||||
| "Setup has failed to validate the product key" | Recreate the ISO with the latest WinUtil. The creator now removes stale `PID.txt`, writes `sources\ei.cfg`, and pins the selected image in `autounattend.xml` so setup does not use an embedded OEM key for a different edition |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -46,12 +46,17 @@ function Find-TweaksByNameOrDescription {
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 2. GET REFERENCE TO TWEAKS PANEL
|
||||
# 2. GET REFERENCE TO TWEAKS OR APPX PANEL
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
$panelName = "tweakspanel"
|
||||
if ($null -ne $Sync.currentTab -and $Sync.currentTab -eq "AppX") {
|
||||
$panelName = "appxpanel"
|
||||
}
|
||||
|
||||
$tweaksPanel = $null
|
||||
try {
|
||||
$tweaksPanel = $Sync.Form.FindName("tweakspanel")
|
||||
$tweaksPanel = $Sync.Form.FindName($panelName)
|
||||
}
|
||||
catch {
|
||||
# Silent return - panel not found or disposed
|
||||
|
||||
@@ -8,9 +8,21 @@ Function Install-WinUtilProgramWinget {
|
||||
[string[]]$Programs
|
||||
)
|
||||
|
||||
if ($Action -eq 'Install') {
|
||||
Start-Process -FilePath winget -ArgumentList "install $Programs --accept-package-agreements --source winget --silent" -NoNewWindow -Wait
|
||||
} else {
|
||||
Start-Process -FilePath winget -ArgumentList "uninstall $Programs --source winget --silent" -NoNewWindow -Wait
|
||||
foreach ($program in $Programs) {
|
||||
if ([string]::IsNullOrWhiteSpace($program) -or $program -eq "na") {
|
||||
continue
|
||||
}
|
||||
|
||||
$source = "winget"
|
||||
if ($program.StartsWith("msstore:", [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$source = "msstore"
|
||||
$program = $program.Substring("msstore:".Length)
|
||||
}
|
||||
|
||||
if ($Action -eq 'Install') {
|
||||
Start-Process -FilePath winget -ArgumentList @("install", "--id", $program, "--accept-package-agreements", "--accept-source-agreements", "--source", $source, "--silent") -NoNewWindow -Wait
|
||||
} else {
|
||||
Start-Process -FilePath winget -ArgumentList @("uninstall", "--id", $program, "--source", $source, "--silent") -NoNewWindow -Wait
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,16 @@ Function Invoke-WinUtilCurrentSystem {
|
||||
|
||||
$originalEncoding = [Console]::OutputEncoding
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
||||
$Sync.InstalledPrograms = winget list -s winget | Select-Object -skip 3 | ConvertFrom-String -PropertyNames "Name", "Id", "Version", "Available" -Delimiter '\s{2,}'
|
||||
$Sync.InstalledPrograms = @("winget", "msstore") | ForEach-Object {
|
||||
winget list -s $psitem | Select-Object -skip 3 | ConvertFrom-String -PropertyNames "Name", "Id", "Version", "Available" -Delimiter '\s{2,}'
|
||||
}
|
||||
[Console]::OutputEncoding = $originalEncoding
|
||||
|
||||
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
|
||||
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
|
||||
$dependencies = @($sync.configs.applications.$($psitem.Key).winget -split ";")
|
||||
$dependencies = @($sync.configs.applications.$($psitem.Key).winget -split ";") | ForEach-Object {
|
||||
$psitem -replace "^msstore:", ""
|
||||
}
|
||||
|
||||
if ($dependencies[-1] -in $sync.InstalledPrograms.Id) {
|
||||
Write-Output $psitem.name
|
||||
|
||||
@@ -213,6 +213,59 @@ function Invoke-WinUtilISOModify {
|
||||
})
|
||||
}
|
||||
|
||||
function Get-WinUtilEditionIdFromName {
|
||||
param([string]$EditionName)
|
||||
|
||||
$normalizedName = ($EditionName -replace '^Windows\s+11\s+', '').Trim()
|
||||
switch -Regex ($normalizedName) {
|
||||
'^Home Single Language$' { return 'CoreSingleLanguage' }
|
||||
'^Home N$' { return 'CoreN' }
|
||||
'^Home$' { return 'Core' }
|
||||
'^Pro for Workstations N$' { return 'ProfessionalWorkstationN' }
|
||||
'^Pro for Workstations$' { return 'ProfessionalWorkstation' }
|
||||
'^Pro Education N$' { return 'ProfessionalEducationN' }
|
||||
'^Pro Education$' { return 'ProfessionalEducation' }
|
||||
'^Pro N$' { return 'ProfessionalN' }
|
||||
'^Pro$' { return 'Professional' }
|
||||
'^Education N$' { return 'EducationN' }
|
||||
'^Education$' { return 'Education' }
|
||||
'^Enterprise LTSC N$' { return 'EnterpriseSN' }
|
||||
'^Enterprise LTSC$' { return 'EnterpriseS' }
|
||||
'^Enterprise N$' { return 'EnterpriseN' }
|
||||
'^Enterprise$' { return 'Enterprise' }
|
||||
default { return '' }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-WinUtilMountedImageEditionId {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$MountDir,
|
||||
[string]$EditionName,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
try {
|
||||
$dismOutput = & dism /English "/Image:$MountDir" /Get-CurrentEdition 2>&1
|
||||
foreach ($line in $dismOutput) {
|
||||
if ($line -match '^\s*Current Edition\s*:\s*(.+?)\s*$') {
|
||||
$editionId = $Matches[1].Trim()
|
||||
if ($editionId) {
|
||||
if ($Logger) { $null = $Logger.Invoke("Detected mounted image EditionID: $editionId") }
|
||||
return $editionId
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if ($Logger) { $null = $Logger.Invoke("Warning: could not detect mounted image EditionID with DISM: $_") }
|
||||
}
|
||||
|
||||
$fallbackEditionId = Get-WinUtilEditionIdFromName -EditionName $EditionName
|
||||
if ($fallbackEditionId -and $Logger) {
|
||||
$null = $Logger.Invoke("Using fallback EditionID '$fallbackEditionId' from selected edition name.")
|
||||
}
|
||||
return $fallbackEditionId
|
||||
}
|
||||
|
||||
function Get-DismImageInfoMap {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ImagePath,
|
||||
@@ -322,9 +375,10 @@ function Invoke-WinUtilISOModify {
|
||||
Log "Mounting install.wim (Index ${selectedWimIndex}: $selectedEditionName) at $mountDir..."
|
||||
Mount-WindowsImage -ImagePath $localWim -Index $selectedWimIndex -Path $mountDir
|
||||
SetProgress "Modifying install.wim..." 45
|
||||
$selectedEditionId = Get-WinUtilMountedImageEditionId -MountDir $mountDir -EditionName $selectedEditionName -Logger ${function:Log}
|
||||
|
||||
Log "Applying WinUtil modifications to install.wim..."
|
||||
Invoke-WinUtilISOScript -ScratchDir $mountDir -ISOContentsDir $isoContents -AutoUnattendXml $autounattendContent -InjectCurrentSystemDrivers $injectDrivers -Log { param($m) Log $m }
|
||||
Invoke-WinUtilISOScript -ScratchDir $mountDir -ISOContentsDir $isoContents -AutoUnattendXml $autounattendContent -InjectCurrentSystemDrivers $injectDrivers -InstallEditionId $selectedEditionId -InstallImageIndex 1 -Log { param($m) Log $m }
|
||||
|
||||
SetProgress "Cleaning up component store (WinSxS)..." 56
|
||||
Log "Running DISM component store cleanup (/ResetBase)..."
|
||||
|
||||
@@ -34,6 +34,16 @@ function Invoke-WinUtilISOScript {
|
||||
them into install.wim and boot.wim index 2 (Windows Setup PE).
|
||||
Defaults to $false.
|
||||
|
||||
.PARAMETER InstallEditionId
|
||||
Optional. Windows edition ID for the selected image, for example Professional
|
||||
or Core. Used to write sources\ei.cfg so setup does not fall back to an
|
||||
embedded firmware product key for a different edition.
|
||||
|
||||
.PARAMETER InstallImageIndex
|
||||
Optional. Image index that setup should install from the final install.wim.
|
||||
Win11 Creator exports the selected edition to a single-image WIM, so this
|
||||
defaults to 1.
|
||||
|
||||
.PARAMETER Log
|
||||
Optional ScriptBlock for progress/status logging. Receives a single [string] argument.
|
||||
|
||||
@@ -56,6 +66,8 @@ function Invoke-WinUtilISOScript {
|
||||
[string]$ISOContentsDir = "",
|
||||
[string]$AutoUnattendXml = "",
|
||||
[bool]$InjectCurrentSystemDrivers = $false,
|
||||
[string]$InstallEditionId = "",
|
||||
[int]$InstallImageIndex = 1,
|
||||
[scriptblock]$Log = { param($m) Write-Output $m }
|
||||
)
|
||||
|
||||
@@ -108,6 +120,146 @@ function Invoke-WinUtilISOScript {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-WinUtilISOScriptChildElement {
|
||||
param (
|
||||
[Parameter(Mandatory)][System.Xml.XmlElement]$Parent,
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
[Parameter(Mandatory)][string]$NamespaceUri
|
||||
)
|
||||
|
||||
foreach ($childNode in $Parent.ChildNodes) {
|
||||
if ($childNode.NodeType -eq [System.Xml.XmlNodeType]::Element -and
|
||||
$childNode.LocalName -eq $Name -and
|
||||
$childNode.NamespaceURI -eq $NamespaceUri) {
|
||||
return [System.Xml.XmlElement]$childNode
|
||||
}
|
||||
}
|
||||
|
||||
$childElement = $Parent.OwnerDocument.CreateElement($Name, $NamespaceUri)
|
||||
[void]$Parent.AppendChild($childElement)
|
||||
return $childElement
|
||||
}
|
||||
|
||||
function ConvertTo-WinUtilISOAnswerFile {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$XmlContent,
|
||||
[int]$ImageIndex = 1
|
||||
)
|
||||
|
||||
if ($ImageIndex -lt 1) { $ImageIndex = 1 }
|
||||
|
||||
$unattendNs = "urn:schemas-microsoft-com:unattend"
|
||||
$wcmNs = "http://schemas.microsoft.com/WMIConfig/2002/State"
|
||||
|
||||
$xmlDoc = [xml]::new()
|
||||
$xmlDoc.PreserveWhitespace = $true
|
||||
$xmlDoc.LoadXml($XmlContent)
|
||||
|
||||
if ($xmlDoc.DocumentElement.NamespaceURI -ne $unattendNs) {
|
||||
throw "Unexpected autounattend.xml namespace: $($xmlDoc.DocumentElement.NamespaceURI)"
|
||||
}
|
||||
|
||||
if (-not $xmlDoc.DocumentElement.HasAttribute("xmlns:wcm")) {
|
||||
$xmlDoc.DocumentElement.SetAttribute("wcm", "http://www.w3.org/2000/xmlns/", $wcmNs)
|
||||
}
|
||||
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace("u", $unattendNs)
|
||||
|
||||
$windowsPESettings = $xmlDoc.SelectSingleNode('/u:unattend/u:settings[@pass="windowsPE"]', $nsMgr)
|
||||
if (-not $windowsPESettings) {
|
||||
$windowsPESettings = $xmlDoc.CreateElement("settings", $unattendNs)
|
||||
$windowsPESettings.SetAttribute("pass", "windowsPE")
|
||||
[void]$xmlDoc.DocumentElement.PrependChild($windowsPESettings)
|
||||
}
|
||||
|
||||
$setupComponent = $windowsPESettings.SelectSingleNode('u:component[@name="Microsoft-Windows-Setup"]', $nsMgr)
|
||||
if (-not $setupComponent) {
|
||||
$setupComponent = $xmlDoc.CreateElement("component", $unattendNs)
|
||||
$setupComponent.SetAttribute("name", "Microsoft-Windows-Setup")
|
||||
$setupComponent.SetAttribute("processorArchitecture", "amd64")
|
||||
$setupComponent.SetAttribute("publicKeyToken", "31bf3856ad364e35")
|
||||
$setupComponent.SetAttribute("language", "neutral")
|
||||
$setupComponent.SetAttribute("versionScope", "nonSxS")
|
||||
[void]$windowsPESettings.AppendChild($setupComponent)
|
||||
}
|
||||
|
||||
$productKeyNodes = @($setupComponent.SelectNodes("u:UserData/u:ProductKey", $nsMgr))
|
||||
foreach ($productKeyNode in $productKeyNodes) {
|
||||
$keyNode = $productKeyNode.SelectSingleNode("u:Key", $nsMgr)
|
||||
$keyValue = if ($keyNode) { $keyNode.InnerText.Trim() } else { "" }
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($keyValue) -or $keyValue -eq "00000-00000-00000-00000-00000") {
|
||||
[void]$productKeyNode.ParentNode.RemoveChild($productKeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
$imageInstall = Get-WinUtilISOScriptChildElement -Parent $setupComponent -Name "ImageInstall" -NamespaceUri $unattendNs
|
||||
$osImage = Get-WinUtilISOScriptChildElement -Parent $imageInstall -Name "OSImage" -NamespaceUri $unattendNs
|
||||
$installFrom = Get-WinUtilISOScriptChildElement -Parent $osImage -Name "InstallFrom" -NamespaceUri $unattendNs
|
||||
|
||||
$existingMetadataNodes = @($installFrom.SelectNodes("u:MetaData", $nsMgr))
|
||||
foreach ($metadataNode in $existingMetadataNodes) {
|
||||
[void]$installFrom.RemoveChild($metadataNode)
|
||||
}
|
||||
|
||||
$metadata = $xmlDoc.CreateElement("MetaData", $unattendNs)
|
||||
$actionAttribute = $xmlDoc.CreateAttribute("wcm", "action", $wcmNs)
|
||||
$actionAttribute.Value = "add"
|
||||
[void]$metadata.Attributes.Append($actionAttribute)
|
||||
|
||||
$keyElement = $xmlDoc.CreateElement("Key", $unattendNs)
|
||||
$keyElement.InnerText = "/IMAGE/INDEX"
|
||||
[void]$metadata.AppendChild($keyElement)
|
||||
|
||||
$valueElement = $xmlDoc.CreateElement("Value", $unattendNs)
|
||||
$valueElement.InnerText = [string]$ImageIndex
|
||||
[void]$metadata.AppendChild($valueElement)
|
||||
|
||||
[void]$installFrom.AppendChild($metadata)
|
||||
|
||||
return $xmlDoc.OuterXml
|
||||
}
|
||||
|
||||
function Write-WinUtilISOEditionConfig {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$ContentRoot,
|
||||
[string]$EditionId,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
if (-not (Test-Path $ContentRoot)) {
|
||||
return
|
||||
}
|
||||
|
||||
$sourcesDir = Join-Path $ContentRoot "sources"
|
||||
New-Item -Path $sourcesDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
$pidPath = Join-Path $sourcesDir "PID.txt"
|
||||
if (Test-Path $pidPath) {
|
||||
Remove-Item -Path $pidPath -Force
|
||||
& $Logger "Removed sources\PID.txt so setup will not force a stale or mismatched product key."
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($EditionId)) {
|
||||
& $Logger "Warning: selected edition ID is unknown - skipping sources\ei.cfg fallback."
|
||||
return
|
||||
}
|
||||
|
||||
$eiCfgPath = Join-Path $sourcesDir "ei.cfg"
|
||||
$eiCfg = @"
|
||||
[EditionID]
|
||||
$EditionId
|
||||
[Channel]
|
||||
Retail
|
||||
[VL]
|
||||
0
|
||||
"@.Trim()
|
||||
|
||||
Set-Content -Path $eiCfgPath -Value $eiCfg -Encoding ASCII -Force
|
||||
& $Logger "Written sources\ei.cfg for EditionID '$EditionId'."
|
||||
}
|
||||
|
||||
# -- 1. Remove provisioned AppX packages ----------------------------------
|
||||
& $Log "Removing provisioned AppX packages..."
|
||||
|
||||
@@ -218,9 +370,17 @@ function Invoke-WinUtilISOScript {
|
||||
Set-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' 'BypassNRO' 'REG_DWORD' '1'
|
||||
|
||||
if ($AutoUnattendXml) {
|
||||
$preparedAutoUnattendXml = $AutoUnattendXml
|
||||
try {
|
||||
$preparedAutoUnattendXml = ConvertTo-WinUtilISOAnswerFile -XmlContent $AutoUnattendXml -ImageIndex $InstallImageIndex
|
||||
& $Log "Prepared autounattend.xml to install image index $InstallImageIndex without forcing a product key."
|
||||
} catch {
|
||||
& $Log "Warning: could not prepare autounattend.xml image selection: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
$xmlDoc = [xml]::new()
|
||||
$xmlDoc.LoadXml($AutoUnattendXml)
|
||||
$xmlDoc.LoadXml($preparedAutoUnattendXml)
|
||||
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace("sg", "https://schneegans.de/windows/unattend-generator/")
|
||||
@@ -251,13 +411,17 @@ function Invoke-WinUtilISOScript {
|
||||
|
||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||
$isoDest = Join-Path $ISOContentsDir "autounattend.xml"
|
||||
Set-Content -Path $isoDest -Value $AutoUnattendXml -Encoding UTF8 -Force
|
||||
Set-Content -Path $isoDest -Value $preparedAutoUnattendXml -Encoding UTF8 -Force
|
||||
& $Log "Written autounattend.xml to ISO root ($isoDest)."
|
||||
}
|
||||
} else {
|
||||
& $Log "Warning: autounattend.xml content is empty - skipping OOBE bypass file."
|
||||
}
|
||||
|
||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||
Write-WinUtilISOEditionConfig -ContentRoot $ISOContentsDir -EditionId $InstallEditionId -Logger $Log
|
||||
}
|
||||
|
||||
& $Log "Disabling reserved storage..."
|
||||
Set-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager' 'ShippedWithReserves' 'REG_DWORD' '0'
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ function Reset-WPFCheckBoxes {
|
||||
[string]$checkboxfilterpattern = "**"
|
||||
)
|
||||
|
||||
$CheckBoxesToCheck = $sync.selectedApps + $sync.selectedTweaks + $sync.selectedFeatures
|
||||
$CheckBoxesToCheck = $sync.selectedApps + $sync.selectedTweaks + $sync.selectedFeatures + $sync.selectedAppx
|
||||
$CheckBoxes = ($sync.GetEnumerator()).where{ $_.Value -is [System.Windows.Controls.CheckBox] -and $_.Name -notlike "WPFToggle*" -and $_.Name -like "$checkboxfilterpattern"}
|
||||
|
||||
foreach ($CheckBox in $CheckBoxes) {
|
||||
|
||||
@@ -1,52 +1,14 @@
|
||||
function Update-WinUtilSelections {
|
||||
<#
|
||||
function Update-WinUtilSelections ($flatJson) {
|
||||
foreach ($cbkey in $flatJson) {
|
||||
|
||||
.SYNOPSIS
|
||||
Updates the $sync.selected variables with a given preset.
|
||||
|
||||
.PARAMETER flatJson
|
||||
The flattened json list of $sync values to select.
|
||||
#>
|
||||
|
||||
param (
|
||||
$flatJson
|
||||
)
|
||||
|
||||
foreach ($item in $flatJson) {
|
||||
# Ensure each item is treated as a string to handle PSCustomObject from JSON deserialization
|
||||
$cbkey = [string]$item
|
||||
$group = if ($cbkey.StartsWith("WPFInstall")) { "Install" }
|
||||
elseif ($cbkey.StartsWith("WPFTweaks")) { "Tweaks" }
|
||||
elseif ($cbkey.StartsWith("WPFToggle")) { "Toggle" }
|
||||
elseif ($cbkey.StartsWith("WPFFeature")) { "Feature" }
|
||||
else { "na" }
|
||||
|
||||
switch ($group) {
|
||||
"Install" {
|
||||
if (!$sync.selectedApps.Contains($cbkey)) {
|
||||
$sync.selectedApps.Add($cbkey)
|
||||
# The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry
|
||||
[System.Collections.Generic.List[string]]$sync.selectedApps = $sync.SelectedApps | Sort-Object
|
||||
}
|
||||
}
|
||||
"Tweaks" {
|
||||
if (!$sync.selectedTweaks.Contains($cbkey)) {
|
||||
$sync.selectedTweaks.Add($cbkey)
|
||||
}
|
||||
}
|
||||
"Toggle" {
|
||||
if (!$sync.selectedToggles.Contains($cbkey)) {
|
||||
$sync.selectedToggles.Add($cbkey)
|
||||
}
|
||||
}
|
||||
"Feature" {
|
||||
if (!$sync.selectedFeatures.Contains($cbkey)) {
|
||||
$sync.selectedFeatures.Add($cbkey)
|
||||
}
|
||||
}
|
||||
default {
|
||||
Write-Host "Unknown group for checkbox: $($cbkey)"
|
||||
}
|
||||
$listName = switch -Regex ($cbkey) {
|
||||
'^WPFInstall' { 'selectedApps' }
|
||||
'^WPFTweaks' { 'selectedTweaks' }
|
||||
'^WPFToggle' { 'selectedToggles' }
|
||||
'^WPFFeature' { 'selectedFeatures' }
|
||||
'^WPFAppx' { 'selectedAppx' }
|
||||
}
|
||||
|
||||
$sync.$listName.Add($cbkey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
function Invoke-WPFAppxRemoval {
|
||||
if (-not ($sync.selectedAppx)) {
|
||||
[System.Windows.Forms.MessageBox]::Show("No AppX Package selected","Error","OK","Error")
|
||||
return
|
||||
}
|
||||
|
||||
$selected = $sync.selectedAppx
|
||||
$apps = $sync.configs.appxHashtable
|
||||
|
||||
$handle = Invoke-WPFRunspace -ParameterList @(("selected", $selected), ("apps", $apps)) -ScriptBlock {
|
||||
param($selected, $apps)
|
||||
|
||||
$sync.ProcessRunning = $true
|
||||
|
||||
foreach ($key in $selected) {
|
||||
if ($key -eq "WPFAppxMicrosoft_XboxGamingOverlay") {
|
||||
# Making sure Game Bar isn't running
|
||||
Stop-Process -Name GameBarFTServer
|
||||
|
||||
# This stops annoying ms-gamebar popup when launching games.
|
||||
Set-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR -Name AppCaptureEnabled -Value 0
|
||||
}
|
||||
|
||||
if ($key -eq "WPFAppxMicrosoft_WindowsNotepad") {
|
||||
# i hope your having fun reading this
|
||||
Stop-Process -Name dllhost
|
||||
}
|
||||
|
||||
Write-Host "Removing $($apps[$key].Content)"
|
||||
Get-AppxPackage -Name $apps[$key].PackageId -AllUsers | Remove-AppxPackage -AllUsers
|
||||
|
||||
if ($key -eq "WPFAppxMSTeams") {
|
||||
# Uninstalls Microsoft Teams Meeting Add-in for Microsoft Office
|
||||
Get-Package -Name "Microsoft Teams*" -ErrorAction SilentlyContinue | Uninstall-Package -Force
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "================================="
|
||||
Write-Host "-- AppX Removal Finished ---"
|
||||
Write-Host "================================="
|
||||
|
||||
$sync.ProcessRunning = $false
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,22 @@ function Invoke-WPFButton {
|
||||
"WPFUpdatessecurity" {Invoke-WPFUpdatessecurity}
|
||||
"WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"}
|
||||
"WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"}
|
||||
"WPFRemoveSelectedAppx" {Invoke-WPFAppxRemoval}
|
||||
"WPFDefaultAppxSelection" {Invoke-WPFPresets "AppxDefault" -checkboxfilterpattern "WPFAppx*"}
|
||||
"WPFSelectAllAppx" {
|
||||
$sync.configs.appxHashtable.Keys | ForEach-Object {$sync.$_.IsChecked = $true}
|
||||
}
|
||||
"WPFClearAppxSelection" {
|
||||
$sync.configs.appxHashtable.Keys | ForEach-Object {$sync.$_.IsChecked = $false}
|
||||
}
|
||||
"WPFGetInstalledAppx" {
|
||||
$installedAppxPackages = Get-AppxPackage -AllUsers | Select-Object -ExpandProperty Name
|
||||
foreach ($appx in $sync.configs.appxHashtable.GetEnumerator()) {
|
||||
if ($appx.Value.PackageId -in $installedAppxPackages) {
|
||||
$sync.$($appx.Key).IsChecked = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
"WPFCloseButton" {$sync.Form.Close(); Write-Host "Bye bye!"}
|
||||
"WPFMinimizeButton" {$sync.Form.WindowState = [Windows.WindowState]::Minimized}
|
||||
"WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen}
|
||||
|
||||
@@ -44,10 +44,10 @@ function Invoke-WPFImpex {
|
||||
try {
|
||||
$Config = ConfigDialog
|
||||
if ($Config) {
|
||||
$allConfs = ($sync.selectedApps + $sync.selectedTweaks + $sync.selectedToggles + $sync.selectedFeatures) | ForEach-Object { [string]$_ }
|
||||
$allConfs = ($sync.selectedApps + $sync.selectedTweaks + $sync.selectedToggles + $sync.selectedFeatures + $sync.selectedAppx) | ForEach-Object { [string]$_ }
|
||||
if (-not $allConfs) {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"No settings are selected to export. Please select at least one app, tweak, toggle, or feature before exporting.",
|
||||
"No settings are selected to export. Please select at least one app, tweak, toggle, feature, or AppX package before exporting.",
|
||||
"Nothing to Export", "OK", "Warning")
|
||||
return
|
||||
}
|
||||
@@ -86,12 +86,17 @@ function Invoke-WPFImpex {
|
||||
|
||||
# Clear all existing selections before importing so the import replaces
|
||||
# the current state rather than merging with it
|
||||
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedApps = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedToggles = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedFeatures = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
Update-WinUtilSelections -flatJson $flattenedJson
|
||||
|
||||
if ($sync.Form) {
|
||||
Reset-WPFCheckBoxes -doToggles $true
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Error "An error occurred while importing: $_"
|
||||
|
||||
@@ -37,6 +37,7 @@ function Invoke-WPFPresets {
|
||||
switch ($checkboxfilterpattern) {
|
||||
"WPFTweak*" { $sync.selectedTweaks = [System.Collections.Generic.List[string]]::new() }
|
||||
"WPFInstall*" { $sync.selectedApps = [System.Collections.Generic.List[string]]::new() }
|
||||
"WPFAppx*" { $sync.selectedAppx = [System.Collections.Generic.List[string]]::new() }
|
||||
"WPFeatures" { $sync.selectedFeatures = [System.Collections.Generic.List[string]]::new() }
|
||||
"WPFToggle" { $sync.selectedToggles = [System.Collections.Generic.List[string]]::new() }
|
||||
default {}
|
||||
|
||||
@@ -1,88 +1,17 @@
|
||||
function Invoke-WPFSelectedCheckboxesUpdate{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This is a helper function that is called by the Checked and Unchecked events of the Checkboxes.
|
||||
It also Updates the "Selected Apps" selectedAppLabel on the Install Tab to represent the current collection
|
||||
.PARAMETER type
|
||||
Either: Add | Remove
|
||||
.PARAMETER checkboxName
|
||||
should contain the name of the current instance of the checkbox that triggered the Event.
|
||||
Most of the time will be the automatic variable $this.Parent.Tag
|
||||
.EXAMPLE
|
||||
$checkbox.Add_Unchecked({Invoke-WPFSelectedCheckboxesUpdate -type "Remove" -checkboxName $this.Parent.Tag})
|
||||
OR
|
||||
Invoke-WPFSelectedCheckboxesUpdate -type "Add" -checkboxName $specificCheckbox.Parent.Tag
|
||||
#>
|
||||
param (
|
||||
$type,
|
||||
$checkboxName
|
||||
)
|
||||
|
||||
if (($type -ne "Add") -and ($type -ne "Remove"))
|
||||
{
|
||||
Write-Error "Type: $type not implemented"
|
||||
return
|
||||
function Invoke-WPFSelectedCheckboxesUpdate ($type, $checkboxName) {
|
||||
$listName = switch -Regex ($checkboxName) {
|
||||
'^WPFInstall' { 'selectedApps' }
|
||||
'^WPFTweaks' { 'selectedTweaks' }
|
||||
'^WPFToggle' { 'selectedToggles' }
|
||||
'^WPFFeature' { 'selectedFeatures' }
|
||||
'^WPFAppx' { 'selectedAppx' }
|
||||
}
|
||||
|
||||
# Get the actual Name from the selectedAppLabel inside the Checkbox
|
||||
$appKey = $checkboxName
|
||||
$group = if ($appKey.StartsWith("WPFInstall")) { "Install" }
|
||||
elseif ($appKey.StartsWith("WPFTweaks")) { "Tweaks" }
|
||||
elseif ($appKey.StartsWith("WPFToggle")) { "Toggle" }
|
||||
elseif ($appKey.StartsWith("WPFFeature")) { "Feature" }
|
||||
else { "na" }
|
||||
|
||||
switch ($group) {
|
||||
"Install" {
|
||||
if ($type -eq "Add") {
|
||||
if (!$sync.selectedApps.Contains($appKey)) {
|
||||
$sync.selectedApps.Add($appKey)
|
||||
# The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry
|
||||
[System.Collections.Generic.List[string]]$sync.selectedApps = $sync.SelectedApps | Sort-Object
|
||||
}
|
||||
}
|
||||
else{
|
||||
$sync.selectedApps.Remove($appKey)
|
||||
}
|
||||
|
||||
$count = $sync.SelectedApps.Count
|
||||
$sync.WPFselectedAppsButton.Content = "Selected Apps: $count"
|
||||
# On every change, remove all entries inside the Popup Menu. This is done, so we can keep the alphabetical order even if elements are selected in a random way
|
||||
$sync.selectedAppsstackPanel.Children.Clear()
|
||||
$sync.selectedApps | Foreach-Object { Add-SelectedAppsMenuItem -name $($sync.configs.applicationsHashtable.$_.Content) -key $_ }
|
||||
}
|
||||
"Tweaks" {
|
||||
if ($type -eq "Add") {
|
||||
if (!$sync.selectedTweaks.Contains($appKey)) {
|
||||
$sync.selectedTweaks.Add($appKey)
|
||||
}
|
||||
}
|
||||
else{
|
||||
$sync.selectedTweaks.Remove($appKey)
|
||||
}
|
||||
}
|
||||
"Toggle" {
|
||||
if ($type -eq "Add") {
|
||||
if (!$sync.selectedToggles.Contains($appKey)) {
|
||||
$sync.selectedToggles.Add($appKey)
|
||||
}
|
||||
}
|
||||
else{
|
||||
$sync.selectedToggles.Remove($appKey)
|
||||
}
|
||||
}
|
||||
"Feature" {
|
||||
if ($type -eq "Add") {
|
||||
if (!$sync.selectedFeatures.Contains($appKey)) {
|
||||
$sync.selectedFeatures.Add($appKey)
|
||||
}
|
||||
}
|
||||
else{
|
||||
$sync.selectedFeatures.Remove($appKey)
|
||||
}
|
||||
}
|
||||
default {
|
||||
Write-Host "Unknown group for checkbox: $($appKey)"
|
||||
if ($type -eq "Add") {
|
||||
if (-not $sync.$listName.Contains($checkboxName)) {
|
||||
$sync.$listName.Add($checkboxName)
|
||||
}
|
||||
} else {
|
||||
$sync.$listName.Remove($checkboxName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,13 @@ function Invoke-WPFTab {
|
||||
} elseif ($sync.currentTab -eq "Tweaks") {
|
||||
# Reset Tweaks tab filter
|
||||
Find-TweaksByNameOrDescription -SearchString ""
|
||||
} elseif ($sync.currentTab -eq "AppX") {
|
||||
# Reset AppX tab filter
|
||||
Find-TweaksByNameOrDescription -SearchString ""
|
||||
}
|
||||
|
||||
# Show search bar in Install and Tweaks tabs
|
||||
if ($tabNumber -eq 0 -or $tabNumber -eq 1) {
|
||||
# Show search bar in Install, Tweaks, and AppX tabs
|
||||
if ($tabNumber -eq 0 -or $tabNumber -eq 1 -or $tabNumber -eq 5) {
|
||||
$sync.SearchBar.Visibility = "Visible"
|
||||
$searchIcon = ($sync.Form.FindName("SearchBar").Parent.Children | Where-Object { $_ -is [System.Windows.Controls.TextBlock] -and $_.Text -eq [char]0xE721 })[0]
|
||||
if ($searchIcon) {
|
||||
|
||||
@@ -30,5 +30,11 @@ function Invoke-WinUtilAutoRun {
|
||||
BusyWait
|
||||
}
|
||||
|
||||
if ($sync.selectedAppx.Count -gt 0) {
|
||||
Write-Host "Removing AppX packages..."
|
||||
Invoke-WPFAppxRemoval
|
||||
BusyWait
|
||||
}
|
||||
|
||||
Write-Host "Done."
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
function Invoke-WinUtilRemoveEdge {
|
||||
New-Item -Path "$Env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\MicrosoftEdge.exe" -Force
|
||||
|
||||
$Path = Resolve-Path -Path "$Env:ProgramFiles (x86)\Microsoft\Edge\Application\*\Installer\setup.exe" | Select-Object -Last 1
|
||||
Start-Process -FilePath $Path -ArgumentList '--uninstall --system-level --force-uninstall --delete-profile' -Wait
|
||||
|
||||
Write-Host "Microsoft Edge was removed" -ForegroundColor Green
|
||||
}
|
||||
+96
-69
@@ -1,83 +1,110 @@
|
||||
# Import Config Files
|
||||
$global:importedconfigs = @{}
|
||||
Get-ChildItem .\config | Where-Object {$_.Extension -eq ".json"} | ForEach-Object {
|
||||
$global:importedconfigs[$psitem.BaseName] = Get-Content $psitem.FullName | ConvertFrom-Json
|
||||
}
|
||||
|
||||
|
||||
#===========================================================================
|
||||
# Tests - Application Installs
|
||||
# Tests - Config Files
|
||||
#===========================================================================
|
||||
|
||||
Describe "Config Files" -ForEach @(
|
||||
@{
|
||||
name = "applications"
|
||||
config = $('{
|
||||
"winget": "value",
|
||||
"choco": "value",
|
||||
"category": "value",
|
||||
"content": "value",
|
||||
"description": "value",
|
||||
"link": "value"
|
||||
}' | ConvertFrom-Json)
|
||||
},
|
||||
@{
|
||||
name = "tweaks"
|
||||
undo = $true
|
||||
}
|
||||
) {
|
||||
Context "$name config file" {
|
||||
It "Imports with no errors" {
|
||||
$global:importedconfigs.$name | should -Not -BeNullOrEmpty
|
||||
$configRoot = Join-Path $PSScriptRoot "..\config"
|
||||
$configCases = @(
|
||||
Get-ChildItem -Path $configRoot -Filter *.json | ForEach-Object {
|
||||
@{
|
||||
Name = $_.Name
|
||||
Path = $_.FullName
|
||||
}
|
||||
if ($config) {
|
||||
It "Imports should be the correct structure" {
|
||||
$applications = $global:importedconfigs.$name | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
|
||||
$template = $config | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
|
||||
$result = New-Object System.Collections.Generic.List[System.Object]
|
||||
Foreach ($application in $applications) {
|
||||
$compare = $global:importedconfigs.$name.$application | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
|
||||
if (-not $compare) {
|
||||
throw "Comparison object for application '$application' is null."
|
||||
}
|
||||
if (-not $template) {
|
||||
throw "Template object for application '$application' is null."
|
||||
}
|
||||
if ($(Compare-Object $compare $template) -ne $null) {
|
||||
$result.Add($application)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
$result | Select-String "WPF*" | should -BeNullOrEmpty
|
||||
Describe "Config files" {
|
||||
foreach ($configCase in $configCases) {
|
||||
It "imports $($configCase.Name) with no JSON errors" -TestCases $configCase {
|
||||
param([string]$Name, [string]$Path)
|
||||
|
||||
try {
|
||||
Get-Content -Path $Path -Raw | ConvertFrom-Json | Out-Null
|
||||
} catch {
|
||||
throw "Failed to import ${Name}: $_"
|
||||
}
|
||||
}
|
||||
if($undo) {
|
||||
It "Tweaks should contain original Value" {
|
||||
$tweaks = $global:importedconfigs.$name | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
|
||||
$result = New-Object System.Collections.Generic.List[System.Object]
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tweak in $tweaks) {
|
||||
$Originals = @(
|
||||
@{
|
||||
name = "registry"
|
||||
value = "OriginalValue"
|
||||
},
|
||||
@{
|
||||
name = "service"
|
||||
value = "OriginalType"
|
||||
}
|
||||
)
|
||||
Foreach ($original in $Originals) {
|
||||
$TotalCount = ($global:importedconfigs.$name.$tweak.$($original.name)).count
|
||||
$OriginalCount = ($global:importedconfigs.$name.$tweak.$($original.name).$($original.value) | Where-Object {$_}).count
|
||||
if($TotalCount -ne $OriginalCount) {
|
||||
$result.Add("$Tweak,$($original.name)")
|
||||
}
|
||||
}
|
||||
Describe "Applications config" {
|
||||
$testCase = @{ Path = (Join-Path $configRoot "applications.json") }
|
||||
|
||||
It "contains at least one application" -TestCases $testCase {
|
||||
param([string]$Path)
|
||||
|
||||
$applications = Get-Content -Path $Path -Raw | ConvertFrom-Json
|
||||
$applicationEntries = @($applications.PSObject.Properties)
|
||||
|
||||
if ($applicationEntries.Count -eq 0) {
|
||||
throw "applications.json does not contain any application entries."
|
||||
}
|
||||
}
|
||||
|
||||
It "contains required display fields and at least one install source" -TestCases $testCase {
|
||||
param([string]$Path)
|
||||
|
||||
$applications = Get-Content -Path $Path -Raw | ConvertFrom-Json
|
||||
$requiredFields = @("category", "content", "description", "link")
|
||||
$invalidEntries = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
foreach ($entry in $applications.PSObject.Properties) {
|
||||
$entryFields = @($entry.Value.PSObject.Properties.Name)
|
||||
|
||||
foreach ($field in $requiredFields) {
|
||||
if ($entryFields -notcontains $field -or [string]::IsNullOrWhiteSpace([string]$entry.Value.$field)) {
|
||||
$invalidEntries.Add("$($entry.Name) missing $field")
|
||||
}
|
||||
$result | Select-String "WPF*" | should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
$hasInstallSource = $false
|
||||
foreach ($sourceField in @("winget", "choco")) {
|
||||
if ($entryFields -contains $sourceField -and -not [string]::IsNullOrWhiteSpace([string]$entry.Value.$sourceField)) {
|
||||
$hasInstallSource = $true
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $hasInstallSource) {
|
||||
$invalidEntries.Add("$($entry.Name) missing winget/choco install source")
|
||||
}
|
||||
}
|
||||
|
||||
if ($invalidEntries.Count -gt 0) {
|
||||
throw ($invalidEntries -join "`n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Tweaks config" {
|
||||
$testCase = @{ Path = (Join-Path $configRoot "tweaks.json") }
|
||||
|
||||
It "contains undo metadata for registry and service actions" -TestCases $testCase {
|
||||
param([string]$Path)
|
||||
|
||||
$tweaks = Get-Content -Path $Path -Raw | ConvertFrom-Json
|
||||
$invalidTweaks = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
foreach ($tweak in $tweaks.PSObject.Properties) {
|
||||
foreach ($registryEntry in @($tweak.Value.registry)) {
|
||||
if ($null -eq $registryEntry) { continue }
|
||||
|
||||
if ($registryEntry.PSObject.Properties.Name -notcontains "OriginalValue" -or
|
||||
[string]::IsNullOrWhiteSpace([string]$registryEntry.OriginalValue)) {
|
||||
$invalidTweaks.Add("$($tweak.Name),registry")
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($serviceEntry in @($tweak.Value.service)) {
|
||||
if ($null -eq $serviceEntry) { continue }
|
||||
|
||||
if ($serviceEntry.PSObject.Properties.Name -notcontains "OriginalType" -or
|
||||
[string]::IsNullOrWhiteSpace([string]$serviceEntry.OriginalType)) {
|
||||
$invalidTweaks.Add("$($tweak.Name),service")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($invalidTweaks.Count -gt 0) {
|
||||
throw ($invalidTweaks -join "`n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+54
-36
@@ -1,53 +1,71 @@
|
||||
#===========================================================================
|
||||
# Tests - Functions
|
||||
#===========================================================================
|
||||
Describe "Comprehensive Checks for PS1 Files in Functions Folder" {
|
||||
BeforeAll {
|
||||
# Get all .ps1 files in the functions folder
|
||||
$ps1Files = Get-ChildItem -Path ./functions -Filter *.ps1 -Recurse
|
||||
|
||||
$functionRoot = Join-Path $PSScriptRoot "..\functions"
|
||||
$functionCases = @(
|
||||
Get-ChildItem -Path $functionRoot -Filter *.ps1 -Recurse | ForEach-Object {
|
||||
@{
|
||||
Name = $_.Name
|
||||
Path = $_.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
foreach ($file in $ps1Files) {
|
||||
Context "Checking $($file.Name)" {
|
||||
It "Should import without errors" {
|
||||
{ . $file.FullName } | Should -Not -Throw
|
||||
}
|
||||
Describe "Function source files" {
|
||||
foreach ($functionCase in $functionCases) {
|
||||
Context "Checking $($functionCase.Path)" {
|
||||
It "has no parser errors" -TestCases $functionCase {
|
||||
param([string]$Path)
|
||||
|
||||
It "Should have no syntax errors" {
|
||||
$tokens = $null
|
||||
$syntaxErrors = $null
|
||||
$null = [System.Management.Automation.PSParser]::Tokenize((Get-Content -Path $file.FullName -Raw), [ref]$syntaxErrors)
|
||||
$syntaxErrors.Count | Should -Be 0
|
||||
}
|
||||
[System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$tokens, [ref]$syntaxErrors) | Out-Null
|
||||
|
||||
It "Should not use deprecated cmdlets or aliases" {
|
||||
$content = Get-Content -Path $file.FullName -Raw
|
||||
# Example check for a known deprecated cmdlet or alias
|
||||
$content | Should -Not -Match 'DeprecatedCmdlet'
|
||||
# Add more checks as needed
|
||||
}
|
||||
|
||||
It "Should follow naming conventions for functions" {
|
||||
$functions = (Get-Command -Path $file.FullName).Name
|
||||
foreach ($function in $functions) {
|
||||
$function | Should -Match '^[a-z]+(-[a-z]+)*$' # Enforce lower-kebab-case
|
||||
if ($syntaxErrors.Count -ne 0) {
|
||||
throw ($syntaxErrors | Out-String)
|
||||
}
|
||||
}
|
||||
|
||||
It "Should define mandatory parameters for all functions" {
|
||||
. $file.FullName
|
||||
$functions = (Get-Command -Path $file.FullName).Name
|
||||
foreach ($function in $functions) {
|
||||
$parameters = (Get-Command -Name $function).Parameters.Values
|
||||
$mandatoryParams = $parameters | Where-Object { $_.Attributes.Mandatory -eq $true }
|
||||
$mandatoryParams.Count | Should -BeGreaterThan 0
|
||||
It "defines top-level functions with approved verb-noun names" -TestCases $functionCase {
|
||||
param([string]$Path)
|
||||
|
||||
$tokens = $null
|
||||
$syntaxErrors = $null
|
||||
$approvedVerbs = (Get-Verb).Verb
|
||||
$ast = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$tokens, [ref]$syntaxErrors)
|
||||
if ($syntaxErrors.Count -ne 0) {
|
||||
throw ($syntaxErrors | Out-String)
|
||||
}
|
||||
|
||||
$topLevelFunctions = @(
|
||||
$ast.EndBlock.Statements |
|
||||
Where-Object { $_ -is [System.Management.Automation.Language.FunctionDefinitionAst] }
|
||||
)
|
||||
|
||||
if ($topLevelFunctions.Count -eq 0) {
|
||||
throw "No top-level function was found in $Path."
|
||||
}
|
||||
|
||||
foreach ($function in $topLevelFunctions) {
|
||||
if ($function.Name -notmatch '^[A-Za-z]+-[A-Za-z0-9]+$') {
|
||||
throw "Function '$($function.Name)' does not use Verb-Noun naming."
|
||||
}
|
||||
|
||||
$verb = ($function.Name -split '-', 2)[0]
|
||||
if ($approvedVerbs -notcontains $verb) {
|
||||
throw "Function '$($function.Name)' does not use an approved PowerShell verb."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
It "Should have all functions available after import" {
|
||||
. $file.FullName
|
||||
$functions = (Get-Command -Path $file.FullName).Name
|
||||
foreach ($function in $functions) {
|
||||
{ Get-Command -Name $function -CommandType Function } | Should -Not -BeNullOrEmpty
|
||||
It "imports without throwing" -TestCases $functionCase {
|
||||
param([string]$Path)
|
||||
|
||||
try {
|
||||
. $Path
|
||||
} catch {
|
||||
throw "Failed to import ${Path}: $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#===========================================================================
|
||||
# Tests - Win11 Creator
|
||||
#===========================================================================
|
||||
|
||||
Describe "Win11 Creator setup media" {
|
||||
It "autounattend template does not force a product key" {
|
||||
$templatePath = Join-Path $PSScriptRoot "..\tools\autounattend.xml"
|
||||
[xml]$xml = Get-Content -Path $templatePath -Raw
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
|
||||
$nsMgr.AddNamespace("u", "urn:schemas-microsoft-com:unattend")
|
||||
|
||||
$productKeyCount = $xml.SelectNodes("//u:ProductKey", $nsMgr).Count
|
||||
if ($productKeyCount -ne 0) {
|
||||
throw "Expected no ProductKey nodes, found $productKeyCount."
|
||||
}
|
||||
}
|
||||
|
||||
It "ISO script accepts selected edition setup metadata" {
|
||||
$isoScriptPath = Join-Path $PSScriptRoot "..\functions\private\Invoke-WinUtilISOScript.ps1"
|
||||
$content = Get-Content -Path $isoScriptPath -Raw
|
||||
|
||||
foreach ($pattern in @(
|
||||
'\[string\]\$InstallEditionId',
|
||||
'\[int\]\$InstallImageIndex',
|
||||
'sources\\ei\.cfg',
|
||||
'PID\.txt'
|
||||
)) {
|
||||
if ($content -notmatch $pattern) {
|
||||
throw "Expected Invoke-WinUtilISOScript.ps1 to match pattern: $pattern"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,11 @@ $sync.configs.applications.PSObject.Properties | ForEach-Object {
|
||||
$sync.configs.applicationsHashtable[$_.Name] = $_.Value
|
||||
}
|
||||
|
||||
$sync.configs.appxHashtable = @{}
|
||||
$sync.configs.appx.PSObject.Properties | ForEach-Object {
|
||||
$sync.configs.appxHashtable[$_.Name] = $_.Value
|
||||
}
|
||||
|
||||
Set-Preferences
|
||||
|
||||
if ($Preset) {
|
||||
@@ -188,6 +193,8 @@ Invoke-WPFUIElements -configVariable $sync.configs.tweaks -targetGridName "tweak
|
||||
|
||||
Invoke-WPFUIElements -configVariable $sync.configs.feature -targetGridName "featurespanel" -columncount 2
|
||||
|
||||
Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2
|
||||
|
||||
# Future implementation: Add Windows Version to updates panel
|
||||
#Invoke-WPFUIElements -configVariable $sync.configs.updates -targetGridName "updatespanel" -columncount 1
|
||||
|
||||
@@ -387,6 +394,9 @@ $searchBarTimer.add_Tick({
|
||||
"Tweaks" {
|
||||
Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text
|
||||
}
|
||||
"AppX" {
|
||||
Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text
|
||||
}
|
||||
}
|
||||
})
|
||||
$sync["SearchBar"].Add_TextChanged({
|
||||
|
||||
@@ -67,6 +67,7 @@ $sync.configs = @{}
|
||||
$sync.Buttons = [System.Collections.Generic.List[PSObject]]::new()
|
||||
$sync.preferences = @{}
|
||||
$sync.ProcessRunning = $false
|
||||
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedApps = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedToggles = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
+92
-24
@@ -42,10 +42,26 @@ function Get-RawJsonBlock {
|
||||
return $null
|
||||
}
|
||||
|
||||
$escapedIndent = [regex]::Escape($startIndent)
|
||||
$endIndex = -1
|
||||
# Use brace-depth tracking to find the closing brace
|
||||
$endIndex = -1
|
||||
$depth = 1 # We're starting inside the opening brace
|
||||
for ($i = ($startIndex + 1); $i -lt $JsonLines.Count; $i++) {
|
||||
if ($JsonLines[$i] -match "^$escapedIndent\}") {
|
||||
$line = $JsonLines[$i]
|
||||
|
||||
# Count braces in this line, ignoring those in strings
|
||||
$inString = $false
|
||||
$chars = $line.ToCharArray()
|
||||
for ($k = 0; $k -lt $chars.Count; $k++) {
|
||||
if ($chars[$k] -eq '"' -and ($k -eq 0 -or $chars[$k-1] -ne '\')) {
|
||||
$inString = -not $inString
|
||||
} elseif (-not $inString) {
|
||||
if ($chars[$k] -eq '{') { $depth++ }
|
||||
elseif ($chars[$k] -eq '}') { $depth-- }
|
||||
}
|
||||
}
|
||||
|
||||
# Found the closing brace of the item
|
||||
if ($depth -eq 0) {
|
||||
$endIndex = $i
|
||||
break
|
||||
}
|
||||
@@ -121,34 +137,86 @@ function Add-LinkAttributeToJson {
|
||||
}
|
||||
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)
|
||||
# Derive indentation used by top-level properties in the item.
|
||||
# Prefer existing property indentation to avoid inheriting bad key indentation.
|
||||
$null = $lines[$startIdx] -match '^(\s*)'
|
||||
$propIndent = $matches[1] + ' '
|
||||
|
||||
# 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
|
||||
$depthProbe = 1
|
||||
for ($p = $startIdx + 1; $p -lt $lines.Count; $p++) {
|
||||
$probeLine = $lines[$p]
|
||||
|
||||
if ($depthProbe -eq 1 -and $probeLine -match '^(\s*)"[^"]+"\s*:') {
|
||||
$propIndent = $matches[1]
|
||||
break
|
||||
}
|
||||
if ($lines[$j] -match '^\s*\}') {
|
||||
$null = $lines[$j] -match '^(\s*)'
|
||||
if ($matches[1].Length -le $propIndentLen) {
|
||||
$closeBraceIdx = $j
|
||||
break
|
||||
|
||||
$inStringProbe = $false
|
||||
$probeChars = $probeLine.ToCharArray()
|
||||
for ($q = 0; $q -lt $probeChars.Count; $q++) {
|
||||
if ($probeChars[$q] -eq '"' -and ($q -eq 0 -or $probeChars[$q-1] -ne '\')) {
|
||||
$inStringProbe = -not $inStringProbe
|
||||
} elseif (-not $inStringProbe) {
|
||||
if ($probeChars[$q] -eq '{') { $depthProbe++ }
|
||||
elseif ($probeChars[$q] -eq '}') { $depthProbe-- }
|
||||
}
|
||||
}
|
||||
|
||||
if ($depthProbe -eq 0) { break }
|
||||
}
|
||||
|
||||
# Scan forward: remove any existing "link" property and find the closing brace.
|
||||
# Use brace-depth tracking to properly handle nested structures like arrays.
|
||||
$closeBraceIdx = -1
|
||||
$depth = 1 # We're starting inside the opening brace of the item
|
||||
$linesToRemove = @()
|
||||
|
||||
for ($j = $startIdx + 1; $j -lt $lines.Count; $j++) {
|
||||
$line = $lines[$j]
|
||||
|
||||
# Check for existing "link" property at top-level (depth 1 before processing braces on this line)
|
||||
# Match at any indentation level (user may have manually changed indentation)
|
||||
if ($depth -eq 1 -and $line -match '^\s*"link"\s*:') {
|
||||
# Mark this line for removal
|
||||
$linesToRemove += $j
|
||||
}
|
||||
|
||||
# Count braces in this line, ignoring those in strings
|
||||
$inString = $false
|
||||
$chars = $line.ToCharArray()
|
||||
for ($k = 0; $k -lt $chars.Count; $k++) {
|
||||
if ($chars[$k] -eq '"' -and ($k -eq 0 -or $chars[$k-1] -ne '\')) {
|
||||
$inString = -not $inString
|
||||
} elseif (-not $inString) {
|
||||
if ($chars[$k] -eq '{') { $depth++ }
|
||||
elseif ($chars[$k] -eq '}') { $depth-- }
|
||||
}
|
||||
}
|
||||
|
||||
# Found the closing brace of the item
|
||||
if ($depth -eq 0) {
|
||||
$closeBraceIdx = $j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $linkUpdated -and $closeBraceIdx -ne -1) {
|
||||
# Insert "link" before the closing brace
|
||||
# Remove old "link" lines in reverse order to preserve indices
|
||||
foreach ($idx in ($linesToRemove | Sort-Object -Descending)) {
|
||||
# If the line before had a trailing comma (from the link property), remove it
|
||||
if ($idx -gt $startIdx) {
|
||||
$prevLine = $lines[$idx - 1]
|
||||
if ($prevLine -match ',\s*$' -and $lines[$idx].Trim() -match '^}') {
|
||||
$lines[$idx - 1] = $prevLine -replace ',\s*$', ''
|
||||
}
|
||||
}
|
||||
$lines.RemoveAt($idx)
|
||||
if ($idx -lt $closeBraceIdx) {
|
||||
$closeBraceIdx--
|
||||
}
|
||||
}
|
||||
|
||||
# Now insert "link" before the closing brace (consistent position for all items)
|
||||
if ($closeBraceIdx -ne -1) {
|
||||
$prevPropIdx = $closeBraceIdx - 1
|
||||
while ($prevPropIdx -gt $startIdx -and $lines[$prevPropIdx].Trim() -eq '') { $prevPropIdx-- }
|
||||
|
||||
|
||||
@@ -1005,6 +1005,14 @@
|
||||
</TextBlock>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<ToggleButton Margin="0,0,5,0" Height="{DynamicResource TabButtonHeight}" Width="Auto" MinWidth="{DynamicResource TabButtonWidth}"
|
||||
Background="{DynamicResource ButtonAppxBackgroundColor}" Foreground="{DynamicResource ButtonAppxForegroundColor}" FontWeight="Bold" Name="WPFTab6BT">
|
||||
<ToggleButton.Content>
|
||||
<TextBlock FontSize="{DynamicResource TabButtonFontSize}" Background="Transparent" Foreground="{DynamicResource ButtonAppxForegroundColor}">
|
||||
<Underline>A</Underline>ppX Removal
|
||||
</TextBlock>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Search Bar and Action Buttons -->
|
||||
@@ -1690,6 +1698,52 @@
|
||||
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="AppX" Visibility="Collapsed" Name="WPFTab6">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Grid.Row="0" Margin="{DynamicResource TabContentMargin}">
|
||||
<Grid Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Vertical" Grid.Row="0" Grid.Column="0" Margin="5">
|
||||
<Label Content="Selections:" FontSize="{DynamicResource FontSize}" VerticalAlignment="Center" Margin="2"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
|
||||
<Button Name="WPFDefaultAppxSelection" Content=" Default " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFGetInstalledAppx" Content=" Get Installed " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFSelectAllAppx" Content=" Select All " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
<Button Name="WPFClearAppxSelection" Content=" Clear Selection " Margin="2" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Name="appxpanel" Grid.Row="1">
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="2" Style="{StaticResource BorderStyle}" Margin="5,15,5,5">
|
||||
<StackPanel Background="{DynamicResource MainBackgroundColor}" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Padding="10" TextWrapping="Wrap" Foreground="{DynamicResource MainForegroundColor}">
|
||||
Note: Select the pre-installed Windows AppX packages you wish to remove and click 'Remove Selected'.
|
||||
<LineBreak/>These packages are removed for the current user and all new user profiles.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<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">
|
||||
<Button Name="WPFRemoveSelectedAppx" Content="Remove Selected" Margin="5" Width="{DynamicResource ButtonWidth}" Height="{DynamicResource ButtonHeight}"/>
|
||||
</WrapPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
Reference in New Issue
Block a user