add restore and backup fix #14

This commit is contained in:
zoicware
2025-09-25 17:59:13 -04:00
committed by GitHub
parent 0d19feca48
commit 96ee42d63c

View File

@@ -13,7 +13,8 @@ param(
'RemoveRecallTasks')]
[array]$Options,
[switch]$AllOptions,
[switch]$revertMode
[switch]$revertMode,
[switch]$backupMode
)
if ($nonInteractive) {
@@ -39,6 +40,10 @@ If (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]:
$arglist = $arglist + ' -revertMode'
}
if ($backupMode) {
$arglist = $arglist + '-backupMode'
}
if ($Options -and $Options.count -ne 0) {
#if options and alloptions is supplied just do all options
@@ -106,6 +111,215 @@ function Run-Trusted([String]$command, $psversion) {
}
# function from: https://github.com/the-loan-wolf/Appx-Backup/blob/master/Appx-Backup.ps1
function Backup-Appx {
param (
[Parameter(Mandatory = $True)]
[string] $WSAppPath,
[Parameter(Mandatory = $True)]
[string] $WSAppOutputPath
)
function Get-FileFromWeb {
param (
# Parameter help description
[Parameter(Mandatory)]
[string]$URL,
# Parameter help description
[Parameter(Mandatory)]
[string]$File
)
Begin {
}
Process {
try {
$storeEAP = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# invoke request
$request = [System.Net.HttpWebRequest]::Create($URL)
$response = $request.GetResponse()
if ($response.StatusCode -eq 401 -or $response.StatusCode -eq 403 -or $response.StatusCode -eq 404) {
throw "Remote file either doesn't exist, is unauthorized, or is forbidden for '$URL'."
}
if ($File -match '^\.\\') {
$File = Join-Path (Get-Location -PSProvider 'FileSystem') ($File -Split '^\.')[1]
}
if ($File -and !(Split-Path $File)) {
$File = Join-Path (Get-Location -PSProvider 'FileSystem') $File
}
if ($File) {
$fileDirectory = $([System.IO.Path]::GetDirectoryName($File))
if (!(Test-Path($fileDirectory))) {
[System.IO.Directory]::CreateDirectory($fileDirectory) | Out-Null
}
}
[long]$fullSize = $response.ContentLength
$fullSizeMB = $fullSize / 1024 / 1024
# define buffer
[byte[]]$buffer = new-object byte[] 1048576
[long]$total = [long]$count = 0
# create reader / writer
$reader = $response.GetResponseStream()
$writer = new-object System.IO.FileStream $File, 'Create'
# start download
$finalBarCount = 0 #show final bar only one time
do {
$count = $reader.Read($buffer, 0, $buffer.Length)
$writer.Write($buffer, 0, $count)
$total += $count
$totalMB = $total / 1024 / 1024
if ($fullSize -gt 0) {
#Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix 'MB'
}
if ($total -eq $fullSize -and $count -eq 0 -and $finalBarCount -eq 0) {
#Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix 'MB' -Complete
$finalBarCount++
}
} while ($count -gt 0)
}
catch {
$ExeptionMsg = $_.Exception.Message
Write-Host "Download breaks with error : $ExeptionMsg"
}
finally {
# cleanup
if ($reader) { $reader.Close() }
if ($writer) { $writer.Flush(); $writer.Close() }
$ErrorActionPreference = $storeEAP
[GC]::Collect()
}
}
}
function Run-Process {
Param ($p, $a)
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $p
$pinfo.Arguments = $a
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$output = $p.StandardOutput.ReadToEnd()
$output += $p.StandardError.ReadToEnd()
$p.WaitForExit()
return $output
}
#tools path
$WSTools = "$env:TEMP\AppxBackupTools-master\tool\x64"
if (!(Test-Path $WSTools)) {
Get-FileFromWeb -URL 'https://github.com/zoicware/AppxBackupTools/archive/refs/heads/master.zip' -File "$env:TEMP\BackupTools.zip"
$ProgressPreference = 'SilentlyContinue'
Expand-Archive "$env:TEMP\BackupTools.zip" -DestinationPath $env:TEMP
}
$WSAppXmlFile = 'AppxManifest.xml'
# read manifest
$FileExists = Test-Path "$WSAppPath\$WSAppXmlFile"
if ($FileExists -eq $False) {
#temp: debug
Write-Status -msg 'ERROR: Windows Store manifest not found.'
}
[xml]$manifest = Get-Content "$WSAppPath\$WSAppXmlFile"
$WSAppName = $manifest.Package.Identity.Name
$WSAppPublisher = $manifest.Package.Identity.Publisher
# prepare
$WSAppFileName = Get-Item $WSAppPath | Select-Object basename
$WSAppFileName = $WSAppFileName.BaseName
if (Test-Path "$WSAppOutputPath\$WSAppFileName.appx") {
Remove-Item "$WSAppOutputPath\$WSAppFileName.appx"
}
$proc = "$WSTools\MakeAppx.exe"
$args = "pack /d ""$WSAppPath"" /p ""$WSAppOutputPath\$WSAppFileName.appx"" /l"
$output = Run-Process $proc $args
if ($output -inotlike '*succeeded*') {
Write-host ' ERROR: Appx creation failed!'
Write-host " proc = $proc"
Write-host " args = $args"
Write-host (' ' + $output)
# Exit
}
if (Test-Path "$WSAppOutputPath\$WSAppFileName.pvk") {
Remove-Item "$WSAppOutputPath\$WSAppFileName.pvk"
}
if (Test-Path "$WSAppOutputPath\$WSAppFileName.cer") {
Remove-Item "$WSAppOutputPath\$WSAppFileName.cer"
}
$proc = "$WSTools\MakeCert.exe"
$args = "-n ""$WSAppPublisher"" -r -a sha256 -len 2048 -cy end -h 0 -eku 1.3.6.1.5.5.7.3.3 -b 01/01/2000 -pe -sv ""$WSAppOutputPath\$WSAppFileName.pvk"" ""$WSAppOutputPath\$WSAppFileName.cer"""
$output = Run-Process $proc $args
if ($output -inotlike '*succeeded*') {
Write-host 'ERROR: Certificate creation failed!'
Write-host "proc = $proc"
Write-host "args = $args"
Write-host (' ' + $output)
# Exit
}
if (Test-Path "$WSAppOutputPath\$WSAppFileName.pfx") {
Remove-Item "$WSAppOutputPath\$WSAppFileName.pfx"
}
$proc = "$WSTools\Pvk2Pfx.exe"
$args = "-pvk ""$WSAppOutputPath\$WSAppFileName.pvk"" -spc ""$WSAppOutputPath\$WSAppFileName.cer"" -pfx ""$WSAppOutputPath\$WSAppFileName.pfx"""
$output = Run-Process $proc $args
if ($output.Length -gt 0) {
Write-host ' ERROR: Certificate conversion to pfx failed!'
Write-host " proc = $proc"
Write-host " args = $args"
Write-host (' ' + $output)
# Exit
}
$proc = "$WSTools\SignTool.exe"
$args = "sign -fd SHA256 -a -f ""$WSAppOutputPath\$WSAppFileName.pfx"" ""$WSAppOutputPath\$WSAppFileName.appx"""
$output = Run-Process $proc $args
if ($output -inotlike '*successfully signed*') {
Write-host 'ERROR: Package signing failed!'
Write-host $output.Length
Write-host "proc = $proc"
Write-host "args = $args"
Write-host (' ' + $output)
# Exit
}
Remove-Item "$WSAppOutputPath\$WSAppFileName.pvk"
Remove-Item "$WSAppOutputPath\$WSAppFileName.pfx"
}
function Write-Status {
param(
[string]$msg,
@@ -158,6 +372,13 @@ else {
$Global:revert = 0
}
if ($backupMode) {
$Global:backup = 1
}
else {
$Global:backup = 0
}
#=====================================================================================
function Add-LogInfo {
@@ -213,6 +434,7 @@ function Disable-Registry-Keys {
#disable additional keys
Reg.exe add 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings' /v 'AutoOpenCopilotLargeScreens' /t REG_DWORD /d @('0', '1')[$revert] /f *>$null
Reg.exe add 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\generativeAI' /v 'Value' /t REG_SZ /d @('Deny', 'Allow')[$revert] /f *>$null
Reg.exe add 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\systemAIModels' /v 'Value' /t REG_SZ /d @('Deny', 'Allow')[$revert] /f *>$null
Reg.exe add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy' /v 'LetAppsAccessGenerativeAI' /t REG_DWORD /d @('2', '1')[$revert] /f *>$null
Reg.exe add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy' /v 'LetAppsAccessSystemAIModels' /t REG_DWORD /d @('2', '1')[$revert] /f *>$null
Reg.exe add 'HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsCopilot' /v 'AllowCopilotRuntime' /t REG_DWORD /d @('0', '1')[$revert] /f *>$null
@@ -228,15 +450,25 @@ function Disable-Registry-Keys {
$backupPath = "$env:USERPROFILE\RemoveWindowsAI\Backup"
$backupFile = 'WSAIFabricSvc.reg'
if ($revert) {
Reg.exe import "$backupPath\$backupFile" >$null
sc.exe create WSAIFabricSvc binPath= "$env:windir\System32\svchost.exe -k WSAIFabricSvcGroup -p" >$null
if (Test-Path "$backupPath\$backupFile") {
Reg.exe import "$backupPath\$backupFile" *>$null
sc.exe create WSAIFabricSvc binPath= "$env:windir\System32\svchost.exe -k WSAIFabricSvcGroup -p" *>$null
}
else {
Write-Status -msg "Path Not Found: $backupPath\$backupFile" -errorOutput $true
}
}
else {
if ($backup) {
Write-Status -msg 'Backing up WSAIFabricSvc...'
#export the service to a reg file before removing it
if (!(Test-Path $backupPath)) {
New-Item $backupPath -Force -ItemType Directory | Out-Null
}
Reg.exe export 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WSAIFabricSvc' "$backupPath\$backupFile" >$null
}
Write-Status -msg 'Removing up WSAIFabricSvc...'
#delete the service
sc.exe delete WSAIFabricSvc *>$null
}
@@ -251,6 +483,7 @@ function Disable-Registry-Keys {
# prob not worth trying to restore shouldnt break any functionality if the rest is restored
# =========================
function Remove-Copilot-Nudges-Keys {
if (!$revert) {
#prefire copilot nudges package by deleting the registry keys
Write-Status -msg 'Removing Copilot Nudges Registry Keys...'
$keys = @(
@@ -292,6 +525,7 @@ function Remove-Copilot-Nudges-Keys {
}
}
}
}
@@ -328,6 +562,32 @@ function Disable-Copilot-Policies {
function Remove-AI-Appx-Packages {
if ($revert) {
#install backedup appx packages
$appxBackup = "$env:USERPROFILE\RemoveWindowsAI\Backup\AppxBackup"
if (Test-Path $appxBackup) {
$files = Get-ChildItem $appxBackup
Write-Status -msg 'Installing Appx Packages...'
foreach ($file in $files) {
if ($file.FullName -like '*.cer') {
#install certs
Import-Certificate -FilePath $file.FullName -CertStoreLocation Cert:\LocalMachine\Root | Out-Null
}
}
#install the packages
$ProgressPreference = 'SilentlyContinue'
foreach ($file in $files) {
if ($file.FullName -like '*.appx') {
Add-AppPackage -Path $file.FullName
}
}
}
else {
Write-Status -msg 'Unable to Find AppxBackup in User Directory!' -errorOutput $true
}
}
else {
#to make this part faster make a txt file in temp with chunck of removal
#code and then just run that from run
#trusted function due to the design of having it hidden from the user
@@ -374,6 +634,26 @@ function Remove-AI-Appx-Packages {
'WindowsWorkload.ImageTextSearch.Stx.3'
)
if ($backup) {
#backup appx packages before removing them
$appxBackup = "$env:USERPROFILE\RemoveWindowsAI\Backup\AppxBackup"
if (!(Test-Path $appxBackup)) {
New-Item $appxBackup -ItemType Directory -Force | Out-Null
}
Write-Status -msg 'Backing Up AI Appx Packages...'
#aix and core ai can not be backed up
$packagesToBackup = get-appxpackage -AllUsers | Where-Object { $aipackages -contains $_.Name } | Where-Object { $_.Name -ne 'MicrosoftWindows.Client.AIX' -and $_.Name -ne 'MicrosoftWindows.Client.CoreAI' }
[System.Windows.MessageBox]::Show('Please Select [NONE] On The Following Windows' , 'INFO', [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Information) *>$null
foreach ($package in $packagesToBackup) {
Backup-Appx -WSAppPath $package.InstallLocation -WSAppOutputPath $appxBackup
}
#cleanup tools
Remove-Item "$env:TEMP\AppxBackupTools-master\tool\x64" -Force -Recurse -ErrorAction SilentlyContinue
Remove-Item "$env:TEMP\BackupTools.zip" -Force -ErrorAction SilentlyContinue
}
$code = @'
$aipackages = @(
'MicrosoftWindows.Client.AIX'
@@ -526,7 +806,10 @@ foreach ($choice in $aipackages) {
}
}
}
function Remove-Recall-Optional-Feature {
if (!$revert) {
#doesnt seem to work just gets stuck (does anyone really want this shit lol)
#Enable-WindowsOptionalFeature -Online -FeatureName 'Recall' -All -NoRestart
#remove recall optional feature
@@ -543,9 +826,11 @@ function Remove-Recall-Optional-Feature {
}
}
}
# not restoring for now shouldnt cause any issues (also may not even be possible to restore)
function Remove-AI-CBS-Packages {
if (!$revert) {
#additional hidden packages
Write-Status -msg 'Removing Additional Hidden AI Packages...'
#unhide the packages from dism, remove owners subkey for removal
@@ -567,11 +852,87 @@ function Remove-AI-CBS-Packages {
}
}
}
}
function Remove-AI-Files {
#prob add params here for each file removal
if ($revert) {
if (Test-Path "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles") {
Write-Status -msg 'Restoring Appx Package Files...'
$paths = Get-Content "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\backupPaths.txt"
foreach ($path in $paths) {
$fileName = Split-Path $path -Leaf
$dest = Split-Path $path -Parent
try {
Move-Item -Path "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\$fileName" -Destination $dest -Force -ErrorAction Stop
}
catch {
$command = "Move-Item -Path `"$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\$fileName`" -Destination `"$dest`" -Force"
Run-Trusted -command $command -psversion $psversion
Start-Sleep 1
}
}
if (Test-Path "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\OfficeAI") {
Write-Status -msg 'Restoring Office AI Files...'
Move-Item "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\OfficeAI\x64\AI" -Destination "$env:ProgramFiles\Microsoft Office\root\vfs\ProgramFilesCommonX64\Microsoft Shared\Office16" -Force
Move-Item "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\OfficeAI\x86\AI" -Destination "$env:ProgramFiles\Microsoft Office\root\vfs\ProgramFilesCommonX64\Microsoft Shared\Office16" -Force
}
Write-Status -msg 'Restoring AI URIs...'
$regs = Get-ChildItem "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\URIHandlers"
foreach ($reg in $regs) {
Reg.exe import $reg.FullName *>$null
}
Write-Status -msg 'Files Restored... You May Need to Repair the Apps Using the Microsoft Store'
}
else {
Write-Status -msg 'Unable to Find Backup Files!' -errorOutput $true
}
}
else {
$aipackages = @(
# 'MicrosoftWindows.Client.Photon'
'MicrosoftWindows.Client.AIX'
'MicrosoftWindows.Client.CoPilot'
'Microsoft.Windows.Ai.Copilot.Provider'
'Microsoft.Copilot'
'Microsoft.MicrosoftOfficeHub'
'MicrosoftWindows.Client.CoreAI'
#ai component packages installed on copilot+ pcs
'WindowsWorkload.Data.Analysis.Stx.1'
'WindowsWorkload.Manager.1'
'WindowsWorkload.PSOnnxRuntime.Stx.2.7'
'WindowsWorkload.PSTokenizer.Stx.2.7'
'WindowsWorkload.QueryBlockList.1'
'WindowsWorkload.QueryProcessor.Data.1'
'WindowsWorkload.QueryProcessor.Stx.1'
'WindowsWorkload.SemanticText.Data.1'
'WindowsWorkload.SemanticText.Stx.1'
'WindowsWorkload.Data.ContentExtraction.Stx.1'
'WindowsWorkload.ScrRegDetection.Data.1'
'WindowsWorkload.ScrRegDetection.Stx.1'
'WindowsWorkload.TextRecognition.Stx.1'
'WindowsWorkload.Data.ImageSearch.Stx.1'
'WindowsWorkload.ImageContentModeration.1'
'WindowsWorkload.ImageContentModeration.Data.1'
'WindowsWorkload.ImageSearch.Data.3'
'WindowsWorkload.ImageSearch.Stx.2'
'WindowsWorkload.ImageSearch.Stx.3'
'WindowsWorkload.ImageTextSearch.Data.3'
'WindowsWorkload.PSOnnxRuntime.Stx.3.2'
'WindowsWorkload.PSTokenizerShared.Data.3.2'
'WindowsWorkload.PSTokenizerShared.Stx.3.2'
'WindowsWorkload.ImageTextSearch.Stx.2'
'WindowsWorkload.ImageTextSearch.Stx.3'
)
Write-Status -msg 'Removing Appx Package Files...'
#-----------------------------------------------------------------------remove files
$appsPath = "$env:SystemRoot\SystemApps"
@@ -634,6 +995,13 @@ function Remove-AI-Files {
}
}
if ($backup) {
Write-Status -msg 'Backing Up AI Files...'
$backupDir = "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles"
if (!(Test-Path $backupDir)) {
New-Item $backupDir -Force -ItemType Directory | Out-Null
}
}
foreach ($Path in $packagesPath) {
#only remove dlls from photon to prevent startmenu from breaking
@@ -643,10 +1011,25 @@ function Remove-AI-Files {
# Start-Sleep 1
# }
# else {
if ($backup) {
$backupFiles = "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\backupPaths.txt"
if (!(Test-Path $backupFiles -PathType Leaf)) {
New-Item $backupFiles -Force -ItemType File | Out-Null
}
try {
Copy-Item -Path $Path -Destination $backupDir -Force -Recurse -ErrorAction Stop
Add-Content -Path $backupFiles -Value $Path
}
catch {
#ignore any errors
}
}
$command = "Remove-item ""$Path"" -force -recurse"
Run-Trusted -command $command -psversion $psversion
Start-Sleep 1
# }
}
@@ -719,6 +1102,22 @@ function Remove-AI-Files {
foreach ($path in $aiPaths) {
if (Test-Path $path -PathType Container -ErrorAction SilentlyContinue) {
if ($backup) {
Write-Status -msg 'Backing Up Office AI Files...'
$backupDir = "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\OfficeAI"
if (!(Test-Path $backupDir)) {
New-Item $backupDir -Force -ItemType Directory | Out-Null
}
if ($path -like '*ProgramFilesCommonX64*') {
$backupDir = "$backupDir\x64"
New-Item $backupDir -Force -ItemType Directory | Out-Null
}
else {
$backupDir = "$backupDir\x86"
New-Item $backupDir -Force -ItemType Directory | Out-Null
}
Copy-Item -Path $path -Destination $backupDir -Force -Recurse -ErrorAction SilentlyContinue
}
Remove-Item $path -Recurse -Force
}
}
@@ -736,10 +1135,22 @@ function Remove-AI-Files {
)
foreach ($uri in $uris) {
if ($backup) {
if (Test-Path $uri) {
$backupDir = "$env:USERPROFILE\RemoveWindowsAI\Backup\AIFiles\URIHandlers"
if (!(Test-Path $backupDir)) {
New-Item $backupDir -Force -ItemType Directory | Out-Null
}
$regExportPath = "$backupDir\$($uri -replace 'registry::HKEY_CLASSES_ROOT\\', '').reg"
Reg.exe export ($uri -replace 'registry::', '') $regExportPath /y *>$null
}
}
Remove-Item $uri -Recurse -Force -ErrorAction SilentlyContinue
}
}
}
function Hide-AI-Components {
#hide ai components in immersive settings
@@ -873,10 +1284,16 @@ else {
$contentRow.Height = [System.Windows.GridLength]::new(1, [System.Windows.GridUnitType]::Star)
$mainGrid.RowDefinitions.Add($contentRow) | Out-Null
# Add this BEFORE your bottom row definition:
$toggleRow = New-Object System.Windows.Controls.RowDefinition
$toggleRow.Height = [System.Windows.GridLength]::new(130) # Fixed height for toggle
$mainGrid.RowDefinitions.Add($toggleRow) | Out-Null
$bottomRow = New-Object System.Windows.Controls.RowDefinition
$bottomRow.Height = [System.Windows.GridLength]::new(60)
$bottomRow.Height = [System.Windows.GridLength]::new(80)
$mainGrid.RowDefinitions.Add($bottomRow) | Out-Null
$title = New-Object System.Windows.Controls.TextBlock
$title.Text = 'Remove Windows AI'
$title.FontSize = 18
@@ -980,9 +1397,189 @@ else {
$stackPanel.Children.Add($optionContainer) | Out-Null
}
#add switches for backup and revert modes
function Add-iOSToggleToUI {
param(
[Parameter(Mandatory = $true)]
[System.Windows.Controls.Panel]$ParentControl,
[bool]$IsChecked = $false,
[string]$Name = 'iOSToggle'
)
$styleXaml = @'
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="CleanToggleStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Width" Value="40"/>
<Setter Property="Height" Value="24"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<!-- Switch Track -->
<Border x:Name="SwitchTrack"
Width="40" Height="24"
Background="#E5E5E7"
CornerRadius="12"
BorderThickness="0">
<!-- Switch Thumb -->
<Border x:Name="SwitchThumb"
Width="20" Height="20"
Background="White"
CornerRadius="10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="2,0,0,0">
<Border.Effect>
<DropShadowEffect Color="#00000040"
Direction="270"
ShadowDepth="1"
BlurRadius="3"
Opacity="0.4"/>
</Border.Effect>
<Border.RenderTransform>
<TranslateTransform x:Name="ThumbTransform" X="0"/>
</Border.RenderTransform>
</Border>
</Border>
</Grid>
<ControlTemplate.Triggers>
<!-- Checked State (ON) -->
<Trigger Property="IsChecked" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Slide thumb to right -->
<DoubleAnimation
Storyboard.TargetName="ThumbTransform"
Storyboard.TargetProperty="X"
To="16"
Duration="0:0:0.2"/>
<!-- Change track color to green -->
<ColorAnimation
Storyboard.TargetName="SwitchTrack"
Storyboard.TargetProperty="Background.Color"
To="#34C759"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<!-- Slide thumb to left -->
<DoubleAnimation
Storyboard.TargetName="ThumbTransform"
Storyboard.TargetProperty="X"
To="0"
Duration="0:0:0.2"/>
<!-- Change track color to gray -->
<ColorAnimation
Storyboard.TargetName="SwitchTrack"
Storyboard.TargetProperty="Background.Color"
To="#E5E5E7"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
'@
$reader = New-Object System.Xml.XmlNodeReader([xml]$styleXaml)
$resourceDict = [Windows.Markup.XamlReader]::Load($reader)
$toggleButton = New-Object System.Windows.Controls.Primitives.ToggleButton
$toggleButton.Name = $Name
$toggleButton.IsChecked = $IsChecked
$toggleButton.Style = $resourceDict['CleanToggleStyle']
$ParentControl.Children.Add($toggleButton) | Out-Null
return $toggleButton
}
$toggleGrid = New-Object System.Windows.Controls.Grid
[System.Windows.Controls.Grid]::SetRow($toggleGrid, 2)
$toggleGrid.Margin = '20,10,55,15'
$row1 = New-Object System.Windows.Controls.RowDefinition
$row1.Height = [System.Windows.GridLength]::Auto
$row2 = New-Object System.Windows.Controls.RowDefinition
$row2.Height = [System.Windows.GridLength]::Auto
$toggleGrid.RowDefinitions.Add($row1) | Out-Null
$toggleGrid.RowDefinitions.Add($row2) | Out-Null
$mainGrid.Children.Add($toggleGrid) | Out-Null
$togglePanel1 = New-Object System.Windows.Controls.StackPanel
$togglePanel1.Orientation = [System.Windows.Controls.Orientation]::Horizontal
$togglePanel1.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left
$togglePanel1.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
$togglePanel1.Margin = New-Object System.Windows.Thickness(0, 0, 0, 10)
[System.Windows.Controls.Grid]::SetRow($togglePanel1, 0)
$toggleLabel1 = New-Object System.Windows.Controls.TextBlock
$toggleLabel1.Text = 'Revert Mode:'
$toggleLabel1.Foreground = [System.Windows.Media.Brushes]::White
$toggleLabel1.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
$toggleLabel1.Margin = New-Object System.Windows.Thickness(0, 0, 10, 0)
$togglePanel1.Children.Add($toggleLabel1) | Out-Null
$revertModeToggle = Add-iOSToggleToUI -ParentControl $togglePanel1 -IsChecked $revert
$toggleGrid.Children.Add($togglePanel1) | Out-Null
$togglePanel2 = New-Object System.Windows.Controls.StackPanel
$togglePanel2.Orientation = [System.Windows.Controls.Orientation]::Horizontal
$togglePanel2.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left
$togglePanel2.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
[System.Windows.Controls.Grid]::SetRow($togglePanel2, 1)
$toggleLabel2 = New-Object System.Windows.Controls.TextBlock
$toggleLabel2.Text = 'Backup Mode:'
$toggleLabel2.Foreground = [System.Windows.Media.Brushes]::White
$toggleLabel2.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
$toggleLabel2.Margin = New-Object System.Windows.Thickness(0, 0, 10, 0)
$togglePanel2.Children.Add($toggleLabel2) | Out-Null
$backupModeToggle = Add-iOSToggleToUI -ParentControl $togglePanel2 -IsChecked $backup
$toggleGrid.Children.Add($togglePanel2) | Out-Null
$backupModeToggle.Add_Checked({
$Global:backup = 1
}) | Out-Null
$backupModeToggle.Add_Unchecked({
$Global:backup = 0
}) | Out-Null
$revertModeToggle.Add_Checked({
$Global:revert = 1
}) | Out-Null
$revertModeToggle.Add_Unchecked({
$Global:revert = 0
}) | Out-Null
$bottomGrid = New-Object System.Windows.Controls.Grid
[System.Windows.Controls.Grid]::SetRow($bottomGrid, 2)
$bottomGrid.Margin = '20,10,20,10'
[System.Windows.Controls.Grid]::SetRow($bottomGrid, 3)
$bottomGrid.Margin = '25,15,25,15'
$leftColumn = New-Object System.Windows.Controls.ColumnDefinition
$leftColumn.Width = [System.Windows.GridLength]::new(1, [System.Windows.GridUnitType]::Star)
@@ -1260,6 +1857,7 @@ else {
}
})
$actionPanel.Children.Add($cancelButton) | Out-Null
$actionPanel.Children.Add($applyButton) | Out-Null