Merge branch 'master' into undo-tweaks

This commit is contained in:
Raphire
2026-03-27 20:34:41 +01:00
21 changed files with 811 additions and 81 deletions

View File

@@ -647,7 +647,7 @@
},
{
"FriendlyName": "Microsoft Edge",
"AppId": "Microsoft.Edge",
"AppId": ["Microsoft.Edge", "XPFFTQ037JWMHS"],
"Description": "Windows' default browser, WARNING: Removing this app also removes the only browser from Windows Sandbox and could affect other apps",
"SelectedByDefault": false,
"Recommendation": "unsafe"

View File

@@ -8,7 +8,7 @@
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="True"
Topmost="False"
ShowInTaskbar="False">
<Border BorderBrush="{DynamicResource BorderColor}"

View File

@@ -8,7 +8,7 @@
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="True"
Topmost="False"
ShowInTaskbar="False">
<Border BorderBrush="{DynamicResource BorderColor}"

View File

@@ -0,0 +1,77 @@
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Select Settings to Import/Export"
Width="440"
SizeToContent="Height"
MaxHeight="501"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="False"
ShowInTaskbar="False">
<Border BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
CornerRadius="8"
Background="{DynamicResource CardBgColor}"
Margin="25">
<Border.Effect>
<DropShadowEffect Color="Black"
Opacity="0.15"
BlurRadius="20"
ShadowDepth="0"
Direction="0"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Title Bar -->
<Grid Grid.Row="0" x:Name="TitleBar" Height="40" Background="Transparent">
<TextBlock x:Name="TitleText"
Foreground="{DynamicResource FgColor}"
FontSize="16"
FontWeight="SemiBold"
VerticalAlignment="Center"
Margin="16,0,0,0"/>
</Grid>
<!-- Content -->
<StackPanel Grid.Row="1" x:Name="ContentPanel" Margin="20,12,20,9">
<TextBlock x:Name="PromptText"
TextWrapping="Wrap"
FontSize="14"
LineHeight="20"
Foreground="{DynamicResource FgColor}"
Margin="0,0,0,14"/>
<!-- Checkboxes are added dynamically at runtime -->
<StackPanel x:Name="CheckboxPanel"/>
</StackPanel>
<!-- Button Footer -->
<Border Grid.Row="2"
Background="{DynamicResource BgColor}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="0,1,0,0"
Padding="16,12"
CornerRadius="0,0,8,8">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="OkButton"
Content="OK"
Height="32" MinWidth="80" Margin="4,0"
Style="{DynamicResource PrimaryButtonStyle}"/>
<Button x:Name="CancelButton"
Content="Cancel"
Height="32" MinWidth="80" Margin="4,0"
Style="{DynamicResource SecondaryButtonStyle}"/>
</StackPanel>
</Border>
</Grid>
</Border>
</Window>

View File

@@ -561,6 +561,29 @@
<Button x:Name="MenuBtn" Content="&#xE700;" FontFamily="Segoe Fluent Icons" FontSize="15" Style="{StaticResource TitlebarButton}" ToolTip="Options" AutomationProperties.Name="Options">
<Button.ContextMenu>
<ContextMenu x:Name="MainMenu">
<ContextMenu.Resources>
<Style x:Key="{x:Static MenuItem.SeparatorStyleKey}" TargetType="Separator">
<Setter Property="Margin" Value="4,6"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Separator">
<Border Height="1" Background="{DynamicResource BorderColor}" SnapsToDevicePixels="True" HorizontalAlignment="Stretch"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.Resources>
<MenuItem x:Name="ImportConfigBtn" Header="Import config" AutomationProperties.Name="Import configuration">
<MenuItem.Icon>
<TextBlock Text="&#xe838;" FontFamily="Segoe Fluent Icons" FontSize="16" Foreground="{DynamicResource FgColor}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="ExportConfigBtn" Header="Export config" AutomationProperties.Name="Export configuration">
<MenuItem.Icon>
<TextBlock Text="&#xe74e;" FontFamily="Segoe Fluent Icons" FontSize="16" Foreground="{DynamicResource FgColor}"/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem x:Name="MenuDocumentation" Header="Documentation" AutomationProperties.Name="Documentation">
<MenuItem.Icon>
<TextBlock Text="&#xe736;" FontFamily="Segoe MDL2 Assets" FontSize="16" Foreground="{DynamicResource FgColor}"/>

View File

@@ -9,7 +9,7 @@
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="True"
Topmost="False"
ShowInTaskbar="False">
<Border BorderBrush="{DynamicResource BorderColor}"

View File

@@ -9,6 +9,9 @@ function RemoveApps {
$appIndex = 0
$appCount = @($appsList).Count
$edgeIds = @('Microsoft.Edge', 'XPFFTQ037JWMHS')
$edgeUninstallSucceeded = $false
$edgeScheduledTaskAdded = $false
Foreach ($app in $appsList) {
if ($script:CancelRequested) {
@@ -25,20 +28,27 @@ function RemoveApps {
Write-Host "Attempting to remove $app..."
# Use WinGet only to remove OneDrive and Edge
if (($app -eq "Microsoft.OneDrive") -or ($app -eq "Microsoft.Edge")) {
if (($app -eq "Microsoft.OneDrive") -or ($edgeIds -contains $app)) {
if ($script:WingetInstalled -eq $false) {
Write-Host "WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red
continue
}
$appName = $app -replace '\.', '_'
$isEdgeId = $edgeIds -contains $app
$appName = if ($isEdgeId) { 'Microsoft_Edge' } else { $app -replace '\.', '_' }
# Uninstall app via WinGet, or create a scheduled task to uninstall it later
if ($script:Params.ContainsKey("User")) {
ImportRegistryFile "Adding scheduled task to uninstall $app for user $(GetUserName)..." "Uninstall_$($appName).reg"
if (-not ($isEdgeId -and $edgeScheduledTaskAdded)) {
ImportRegistryFile "Adding scheduled task to uninstall $app for user $(GetUserName)..." "Uninstall_$($appName).reg"
if ($isEdgeId) { $edgeScheduledTaskAdded = $true }
}
}
elseif ($script:Params.ContainsKey("Sysprep")) {
ImportRegistryFile "Adding scheduled task to uninstall $app after for new users..." "Uninstall_$($appName).reg"
if (-not ($isEdgeId -and $edgeScheduledTaskAdded)) {
ImportRegistryFile "Adding scheduled task to uninstall $app after for new users..." "Uninstall_$($appName).reg"
if ($isEdgeId) { $edgeScheduledTaskAdded = $true }
}
}
else {
# Uninstall app via WinGet
@@ -47,21 +57,35 @@ function RemoveApps {
winget uninstall --accept-source-agreements --disable-interactivity --id $appId
} -ArgumentList $app
If (($app -eq "Microsoft.Edge") -and (Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code")) {
Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red
$wingetFailed = Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code|No installed package found matching input criteria|No package found matching input criteria" -SimpleMatch:$false
if ($isEdgeId) {
if (-not $wingetFailed) {
$edgeUninstallSucceeded = $true
}
if ($script:GuiWindow) {
$result = Show-MessageBox -Message 'Unable to uninstall Microsoft Edge via WinGet. Would you like to forcefully uninstall it? NOT RECOMMENDED!' -Title 'Force Uninstall Microsoft Edge?' -Button 'YesNo' -Icon 'Warning'
# Prompt immediately after the final selected Edge ID attempt (if all attempts failed)
$hasRemainingEdgeIds = $false
if ($appIndex -lt $appCount) {
$remainingApps = @($appsList)[($appIndex)..($appCount - 1)]
$hasRemainingEdgeIds = @($remainingApps | Where-Object { $edgeIds -contains $_ }).Count -gt 0
}
if ($result -eq 'Yes') {
if (-not $hasRemainingEdgeIds -and -not $edgeUninstallSucceeded) {
Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red
if ($script:GuiWindow) {
$result = Show-MessageBox -Message 'Unable to uninstall Microsoft Edge via WinGet. Would you like to forcefully uninstall it? NOT RECOMMENDED!' -Title 'Force Uninstall Microsoft Edge?' -Button 'YesNo' -Icon 'Warning'
if ($result -eq 'Yes') {
Write-Host ""
ForceRemoveEdge
}
}
elseif ($( Read-Host -Prompt "Would you like to forcefully uninstall Microsoft Edge? NOT RECOMMENDED! (y/n)" ) -eq 'y') {
Write-Host ""
ForceRemoveEdge
}
}
elseif ($( Read-Host -Prompt "Would you like to forcefully uninstall Microsoft Edge? NOT RECOMMENDED! (y/n)" ) -eq 'y') {
Write-Host ""
ForceRemoveEdge
}
}
}

View File

@@ -16,24 +16,36 @@ function LoadAppsDetailsFromJson {
}
foreach ($appData in $jsonContent.Apps) {
$appId = $appData.AppId.Trim()
if ($appId.length -eq 0) { continue }
# Handle AppId as array (could be single or multiple IDs)
$appIdArray = if ($appData.AppId -is [array]) { $appData.AppId } else { @($appData.AppId) }
$appIdArray = $appIdArray | ForEach-Object { $_.Trim() } | Where-Object { $_.length -gt 0 }
if ($appIdArray.Count -eq 0) { continue }
if ($OnlyInstalled) {
if (-not ($InstalledList -like ("*$appId*")) -and -not (Get-AppxPackage -Name $appId)) {
continue
}
if (($appId -eq "Microsoft.Edge") -and -not ($InstalledList -like "* Microsoft.Edge *")) {
continue
$isInstalled = $false
foreach ($appId in $appIdArray) {
if (($InstalledList -like ("*$appId*")) -or (Get-AppxPackage -Name $appId)) {
$isInstalled = $true
break
}
if (($appId -eq "Microsoft.Edge") -and ($InstalledList -like "* Microsoft.Edge *")) {
$isInstalled = $true
break
}
}
if (-not $isInstalled) { continue }
}
$friendlyName = if ($appData.FriendlyName) { $appData.FriendlyName } else { $appId }
$displayName = if ($appData.FriendlyName) { "$($appData.FriendlyName) ($appId)" } else { $appId }
# Use first AppId for fallback names, join all for display
$primaryAppId = $appIdArray[0]
$appIdDisplay = $appIdArray -join ', '
$friendlyName = if ($appData.FriendlyName) { $appData.FriendlyName } else { $primaryAppId }
$displayName = if ($appData.FriendlyName) { "$($appData.FriendlyName) ($appIdDisplay)" } else { $appIdDisplay }
$isChecked = if ($InitialCheckedFromJson) { $appData.SelectedByDefault } else { $false }
$apps += [PSCustomObject]@{
AppId = $appId
AppId = $appIdArray
AppIdDisplay = $appIdDisplay
FriendlyName = $friendlyName
DisplayName = $displayName
IsChecked = $isChecked

View File

@@ -16,10 +16,12 @@ function LoadAppsFromFile {
# JSON file format
$jsonContent = Get-Content -Path $appsFilePath -Raw | ConvertFrom-Json
Foreach ($appData in $jsonContent.Apps) {
$appId = $appData.AppId.Trim()
# Handle AppId as array (could be single or multiple IDs)
$appIdArray = if ($appData.AppId -is [array]) { $appData.AppId } else { @($appData.AppId) }
$appIdArray = $appIdArray | ForEach-Object { $_.Trim() } | Where-Object { $_.length -gt 0 }
$selectedByDefault = $appData.SelectedByDefault
if ($selectedByDefault -and $appId.length -gt 0) {
$appsList += $appId
if ($selectedByDefault -and $appIdArray.Count -gt 0) {
$appsList += $appIdArray
}
}
}

View File

@@ -16,10 +16,7 @@ function SaveSettings {
}
}
try {
$settings | ConvertTo-Json -Depth 10 | Set-Content $script:SavedSettingsFilePath
}
catch {
if (-not (SaveToFile -Config $settings -FilePath $script:SavedSettingsFilePath)) {
Write-Output ""
Write-Host "Error: Failed to save settings to LastUsedSettings.json file" -ForegroundColor Red
}

View File

@@ -0,0 +1,19 @@
# Saves configuration JSON to a file.
# Returns $true on success, $false on failure.
function SaveToFile {
param (
[Parameter(Mandatory=$true)]
[hashtable]$Config,
[Parameter(Mandatory=$true)]
[string]$FilePath
)
try {
$Config | ConvertTo-Json -Depth 10 | Set-Content -Path $FilePath -Encoding UTF8
return $true
}
catch {
return $false
}
}

View File

@@ -4,7 +4,7 @@ function ValidateAppslist {
$appsList
)
$supportedAppsList = (LoadAppsDetailsFromJson | ForEach-Object { $_.AppId })
$supportedAppsList = @(LoadAppsDetailsFromJson | ForEach-Object { @($_.AppId) }) | ForEach-Object { $_.Trim() } | Where-Object { $_.Length -gt 0 }
$validatedAppsList = @()
# Validate provided appsList against supportedAppsList

View File

@@ -40,7 +40,7 @@ function Show-AboutDialog {
}
catch { }
}
# Apply theme resources
SetWindowThemeResources -window $aboutWindow -usesDarkMode $usesDarkMode
@@ -83,13 +83,16 @@ function Show-AboutDialog {
})
# Show dialog
$aboutWindow.ShowDialog() | Out-Null
# Hide overlay after dialog closes
if ($overlay) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
try {
$aboutWindow.ShowDialog() | Out-Null
}
finally {
# Hide overlay after dialog closes
if ($overlay) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
}
catch { }
}
catch { }
}
}

View File

@@ -75,7 +75,8 @@ function Show-AppSelectionWindow {
$checkbox = New-Object System.Windows.Controls.CheckBox
$checkbox.Content = $_.DisplayName
$checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $_.DisplayName)
$checkbox.Tag = $_.AppId
$checkbox.Tag = $_.AppIdDisplay
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIds' -Value @($_.AppId)
$checkbox.IsChecked = $_.IsChecked
$checkbox.ToolTip = $_.Description
$checkbox.Style = $window.Resources["AppsPanelCheckBoxStyle"]
@@ -118,9 +119,10 @@ function Show-AppSelectionWindow {
$selectedApps = @()
foreach ($child in $appsPanel.Children) {
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
$selectedApps += $child.Tag
$selectedApps += @($child.AppIds)
}
}
$selectedApps = @($selectedApps | Where-Object { $_ } | Select-Object -Unique)
# Close form without saving if no apps were selected
if ($selectedApps.Count -eq 0) {

View File

@@ -242,13 +242,16 @@ function Show-ApplyModal {
})
# Show dialog
$applyWindow.ShowDialog() | Out-Null
# Hide overlay after dialog closes
if ($overlay) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
try {
$applyWindow.ShowDialog() | Out-Null
}
finally {
# Hide overlay after dialog closes
if ($overlay) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
}
catch { }
}
catch { }
}
}

View File

@@ -0,0 +1,389 @@
function Show-ImportExportConfigWindow {
param (
[System.Windows.Window]$Owner,
[bool]$UsesDarkMode,
[string]$Title,
[string]$Prompt,
[string[]]$Categories = @('Applications', 'System Tweaks', 'Deployment Settings'),
[string[]]$DisabledCategories = @()
)
# Show overlay on owner window
$overlay = $null
$overlayWasAlreadyVisible = $false
try {
$overlay = $Owner.FindName('ModalOverlay')
if ($overlay) {
$overlayWasAlreadyVisible = ($overlay.Visibility -eq 'Visible')
if (-not $overlayWasAlreadyVisible) {
$Owner.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Visible' })
}
}
} catch { }
# Load XAML from schema file
$schemaPath = $script:ImportExportConfigSchema
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
Show-MessageBox -Message 'Import/Export window schema file could not be found.' -Title 'Error' -Button 'OK' -Icon 'Error' -Owner $Owner | Out-Null
if ($overlay -and -not $overlayWasAlreadyVisible) {
try { $Owner.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' }) } catch { }
}
return $null
}
$xaml = Get-Content -Path $schemaPath -Raw
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
try {
$dlg = [System.Windows.Markup.XamlReader]::Load($reader)
}
finally {
$reader.Close()
}
$dlg.Owner = $Owner
SetWindowThemeResources -window $dlg -usesDarkMode $UsesDarkMode
# Copy the CheckBox default style from the main window so checkboxes get the themed template
try {
$mainCheckBoxStyle = $Owner.FindResource([type][System.Windows.Controls.CheckBox])
if ($mainCheckBoxStyle) {
$dlg.Resources.Add([type][System.Windows.Controls.CheckBox], $mainCheckBoxStyle)
}
} catch { }
# Populate named elements
$dlg.Title = $Title
$dlg.FindName('TitleText').Text = $Title
$dlg.FindName('PromptText').Text = $Prompt
$titleBar = $dlg.FindName('TitleBar')
$titleBar.Add_MouseLeftButtonDown({ $dlg.DragMove() })
# Add a themed checkbox per category
$checkboxPanel = $dlg.FindName('CheckboxPanel')
$checkboxes = @{}
foreach ($cat in $Categories) {
$cb = New-Object System.Windows.Controls.CheckBox
$cb.Content = $cat
$cb.IsChecked = $true
$cb.Margin = [System.Windows.Thickness]::new(0,0,0,8)
$cb.FontSize = 14
$cb.Foreground = $dlg.FindResource('FgColor')
if ($DisabledCategories -contains $cat) {
$cb.IsChecked = $false
$cb.IsEnabled = $false
$cb.Opacity = 0.65
$cb.ToolTip = 'No selected settings available in this category.'
}
$checkboxPanel.Children.Add($cb) | Out-Null
$checkboxes[$cat] = $cb
}
$okBtn = $dlg.FindName('OkButton')
$cancelBtn = $dlg.FindName('CancelButton')
$okBtn.Add_Click({ $dlg.Tag = 'OK'; $dlg.Close() })
$cancelBtn.Add_Click({ $dlg.Tag = 'Cancel'; $dlg.Close() })
# Handle Escape key
$dlg.Add_KeyDown({
param($s, $e)
if ($e.Key -eq 'Escape') { $dlg.Tag = 'Cancel'; $dlg.Close() }
})
try {
$dlg.ShowDialog() | Out-Null
}
finally {
# Hide overlay
if ($overlay -and -not $overlayWasAlreadyVisible) {
try { $Owner.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' }) } catch { }
}
}
if ($dlg.Tag -ne 'OK') { return $null }
$selected = @()
foreach ($cat in $Categories) {
if ($checkboxes[$cat].IsEnabled -and $checkboxes[$cat].IsChecked) { $selected += $cat }
}
if ($selected.Count -eq 0) { return $null }
return $selected
}
function Get-SelectedApplications {
param (
[System.Windows.Controls.Panel]$AppsPanel
)
$selectedApps = @()
foreach ($child in $AppsPanel.Children) {
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
$selectedApps += $child.Tag
}
}
return $selectedApps
}
function Get-SelectedTweakSettings {
param (
[System.Windows.Window]$Owner,
[hashtable]$UiControlMappings
)
$tweakSettings = @()
if (-not $UiControlMappings) {
return $tweakSettings
}
foreach ($mappingKey in $UiControlMappings.Keys) {
$control = $Owner.FindName($mappingKey)
if (-not $control) { continue }
$mapping = $UiControlMappings[$mappingKey]
if ($control -is [System.Windows.Controls.CheckBox] -and $control.IsChecked) {
if ($mapping.Type -eq 'feature') {
$tweakSettings += @{ Name = $mapping.FeatureId; Value = $true }
}
}
elseif ($control -is [System.Windows.Controls.ComboBox] -and $control.SelectedIndex -gt 0) {
if ($mapping.Type -eq 'group') {
$selectedValue = $mapping.Values[$control.SelectedIndex - 1]
foreach ($fid in $selectedValue.FeatureIds) {
$tweakSettings += @{ Name = $fid; Value = $true }
}
}
elseif ($mapping.Type -eq 'feature') {
$tweakSettings += @{ Name = $mapping.FeatureId; Value = $true }
}
}
}
return $tweakSettings
}
function Get-DeploymentSettings {
param (
[System.Windows.Window]$Owner,
[System.Windows.Controls.ComboBox]$UserSelectionCombo,
[System.Windows.Controls.TextBox]$OtherUsernameTextBox
)
$deploySettings = @(
@{ Name = 'UserSelectionIndex'; Value = $UserSelectionCombo.SelectedIndex }
)
if ($UserSelectionCombo.SelectedIndex -eq 1) {
$deploySettings += @{ Name = 'OtherUsername'; Value = $OtherUsernameTextBox.Text.Trim() }
}
$appRemovalScopeCombo = $Owner.FindName('AppRemovalScopeCombo')
if ($appRemovalScopeCombo) {
$deploySettings += @{ Name = 'AppRemovalScopeIndex'; Value = $appRemovalScopeCombo.SelectedIndex }
}
$restorePointCheckBox = $Owner.FindName('RestorePointCheckBox')
if ($restorePointCheckBox) {
$deploySettings += @{ Name = 'CreateRestorePoint'; Value = [bool]$restorePointCheckBox.IsChecked }
}
$restartExplorerCheckBox = $Owner.FindName('RestartExplorerCheckBox')
if ($restartExplorerCheckBox) {
$deploySettings += @{ Name = 'RestartExplorer'; Value = [bool]$restartExplorerCheckBox.IsChecked }
}
return $deploySettings
}
function Get-AvailableImportExportCategories {
param (
$Config
)
$availableCategories = @()
if ($Config.Apps) { $availableCategories += 'Applications' }
if ($Config.Tweaks) { $availableCategories += 'System Tweaks' }
if ($Config.Deployment) { $availableCategories += 'Deployment Settings' }
return $availableCategories
}
function Apply-ImportedApplications {
param (
[System.Windows.Controls.Panel]$AppsPanel,
[string[]]$AppIds
)
foreach ($child in $AppsPanel.Children) {
if ($child -is [System.Windows.Controls.CheckBox]) {
$child.IsChecked = ($AppIds -contains $child.Tag)
}
}
}
function Apply-ImportedTweakSettings {
param (
[System.Windows.Window]$Owner,
[hashtable]$UiControlMappings,
[array]$TweakSettings
)
$settingsJson = [PSCustomObject]@{ Settings = @($TweakSettings) }
ApplySettingsToUiControls -window $Owner -settingsJson $settingsJson -uiControlMappings $UiControlMappings
}
function Apply-ImportedDeploymentSettings {
param (
[System.Windows.Window]$Owner,
[System.Windows.Controls.ComboBox]$UserSelectionCombo,
[System.Windows.Controls.TextBox]$OtherUsernameTextBox,
[array]$DeploymentSettings
)
$lookup = @{}
foreach ($setting in $DeploymentSettings) {
$lookup[$setting.Name] = $setting.Value
}
if ($lookup.ContainsKey('UserSelectionIndex')) {
$UserSelectionCombo.SelectedIndex = [int]$lookup['UserSelectionIndex']
}
if ($lookup.ContainsKey('OtherUsername') -and $UserSelectionCombo.SelectedIndex -eq 1) {
$OtherUsernameTextBox.Text = $lookup['OtherUsername']
}
$appRemovalScopeCombo = $Owner.FindName('AppRemovalScopeCombo')
if ($lookup.ContainsKey('AppRemovalScopeIndex') -and $appRemovalScopeCombo) {
$appRemovalScopeCombo.SelectedIndex = [int]$lookup['AppRemovalScopeIndex']
}
$restorePointCheckBox = $Owner.FindName('RestorePointCheckBox')
if ($lookup.ContainsKey('CreateRestorePoint') -and $restorePointCheckBox) {
$restorePointCheckBox.IsChecked = [bool]$lookup['CreateRestorePoint']
}
$restartExplorerCheckBox = $Owner.FindName('RestartExplorerCheckBox')
if ($lookup.ContainsKey('RestartExplorer') -and $restartExplorerCheckBox) {
$restartExplorerCheckBox.IsChecked = [bool]$lookup['RestartExplorer']
}
}
function Export-Configuration {
param (
[System.Windows.Window]$Owner,
[bool]$UsesDarkMode,
[System.Windows.Controls.Panel]$AppsPanel,
[hashtable]$UiControlMappings,
[System.Windows.Controls.ComboBox]$UserSelectionCombo,
[System.Windows.Controls.TextBox]$OtherUsernameTextBox
)
# Precompute exportable data so empty categories can be disabled in the picker.
$selectedApps = Get-SelectedApplications -AppsPanel $AppsPanel
$tweakSettings = Get-SelectedTweakSettings -Owner $Owner -UiControlMappings $UiControlMappings
$disabledCategories = @()
if ($selectedApps.Count -eq 0) { $disabledCategories += 'Applications' }
if ($tweakSettings.Count -eq 0) { $disabledCategories += 'System Tweaks' }
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Export Configuration' -Prompt 'Select which settings to include in the export:' -DisabledCategories $disabledCategories
if (-not $categories) { return }
$config = @{ Version = '1.0' }
if ($categories -contains 'Applications') {
$config['Apps'] = @($selectedApps)
}
if ($categories -contains 'System Tweaks') {
$config['Tweaks'] = @($tweakSettings)
}
if ($categories -contains 'Deployment Settings') {
$config['Deployment'] = @(Get-DeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox)
}
# Show native save-file dialog
$saveDialog = New-Object Microsoft.Win32.SaveFileDialog
$saveDialog.Title = 'Export Configuration'
$saveDialog.Filter = 'JSON files (*.json)|*.json|All files (*.*)|*.*'
$saveDialog.DefaultExt = '.json'
$saveDialog.FileName = "Win11Debloat-Config-$(Get-Date -Format 'yyyyMMdd').json"
if ($saveDialog.ShowDialog($Owner) -ne $true) { return }
if (SaveToFile -Config $config -FilePath $saveDialog.FileName) {
Show-MessageBox -Message "Configuration exported successfully." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
}
else {
Show-MessageBox -Message "Failed to export configuration" -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
}
}
function Import-Configuration {
param (
[System.Windows.Window]$Owner,
[bool]$UsesDarkMode,
[System.Windows.Controls.Panel]$AppsPanel,
[hashtable]$UiControlMappings,
[System.Windows.Controls.ComboBox]$UserSelectionCombo,
[System.Windows.Controls.TextBox]$OtherUsernameTextBox,
[scriptblock]$OnAppsImported,
[scriptblock]$OnImportCompleted
)
# Show native open-file dialog
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
$openDialog.Title = 'Import Configuration'
$openDialog.Filter = 'JSON files (*.json)|*.json|All files (*.*)|*.*'
$openDialog.DefaultExt = '.json'
if ($openDialog.ShowDialog($Owner) -ne $true) { return }
$config = LoadJsonFile -filePath $openDialog.FileName -expectedVersion '1.0'
if (-not $config) {
Show-MessageBox -Message "Failed to read configuration file" -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
return
}
if (-not $config.Version) {
Show-MessageBox -Message "Invalid configuration file format." -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
return
}
$availableCategories = Get-AvailableImportExportCategories -Config $config
if ($availableCategories.Count -eq 0) {
Show-MessageBox -Message "The configuration file contains no importable data." -Title 'Import Configuration' -Button 'OK' -Icon 'Information' | Out-Null
return
}
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Import Configuration' -Prompt 'Select which settings to import:' -Categories $availableCategories
if (-not $categories) { return }
if ($categories -contains 'Applications' -and $config.Apps) {
$appIds = @(
$config.Apps |
Where-Object { $_ -is [string] } |
ForEach-Object { $_.Trim() } |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
)
Apply-ImportedApplications -AppsPanel $AppsPanel -AppIds $appIds
if ($OnAppsImported) {
& $OnAppsImported
}
}
if ($categories -contains 'System Tweaks' -and $config.Tweaks) {
Apply-ImportedTweakSettings -Owner $Owner -UiControlMappings $UiControlMappings -TweakSettings @($config.Tweaks)
}
if ($categories -contains 'Deployment Settings' -and $config.Deployment) {
Apply-ImportedDeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox -DeploymentSettings @($config.Deployment)
}
Show-MessageBox -Message "Configuration imported successfully." -Title 'Import Configuration' -Button 'OK' -Icon 'Information' | Out-Null
if ($OnImportCompleted) {
& $OnImportCompleted $categories
}
}

View File

@@ -27,6 +27,8 @@ function Show-MainWindow {
$menuReportBug = $window.FindName('MenuReportBug')
$menuLogs = $window.FindName('MenuLogs')
$menuAbout = $window.FindName('MenuAbout')
$importConfigBtn = $window.FindName('ImportConfigBtn')
$exportConfigBtn = $window.FindName('ExportConfigBtn')
# Title bar event handlers
$titleBar.Add_MouseLeftButtonDown({
@@ -67,6 +69,22 @@ function Show-MainWindow {
Show-AboutDialog -Owner $window
})
# --- Import/Export Configuration ---
$exportConfigBtn.Add_Click({
Export-Configuration -Owner $window -UsesDarkMode $usesDarkMode -AppsPanel $appsPanel -UiControlMappings $script:UiControlMappings -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox
})
$importConfigBtn.Add_Click({
Import-Configuration -Owner $window -UsesDarkMode $usesDarkMode -AppsPanel $appsPanel -UiControlMappings $script:UiControlMappings -UserSelectionCombo $userSelectionCombo -OtherUsernameTextBox $otherUsernameTextBox -OnAppsImported { UpdateAppSelectionStatus; UpdatePresetStates } -OnImportCompleted {
$tabControl.SelectedIndex = 3
UpdateNavigationButtons
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
}) | Out-Null
}
})
$closeBtn.Add_Click({
$window.Close()
})
@@ -241,7 +259,7 @@ function Show-MainWindow {
$check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
$presetIds = $this.PresetAppIds
ApplyPresetToApps -MatchFilter { param($c) $presetIds -contains $c.Tag }.GetNewClosure() -Check $check
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $presetIds -contains $_ }).Count -gt 0 }.GetNewClosure() -Check $check
})
}
@@ -254,6 +272,11 @@ function Show-MainWindow {
$script:PendingDefaultMode = $false
# Holds apps data preloaded before ShowDialog() so the first load skips the background job
$script:PreloadedAppData = $null
# Prevent app import until the apps list has finished initial population.
if ($importConfigBtn) {
$importConfigBtn.IsEnabled = $false
}
# Set script-level variable for GUI window reference
$script:GuiWindow = $window
@@ -317,7 +340,7 @@ function Show-MainWindow {
$key = switch ($script:SortColumn) {
'Name' { { $_.AppName } }
'Description' { { $_.AppDescription } }
'AppId' { { $_.Tag } }
'AppId' { { $_.AppIdDisplay } }
}
$sorted = $children | Sort-Object $key -Descending:(-not $script:SortAscending)
$appsPanel.Children.Clear()
@@ -379,14 +402,6 @@ function Show-MainWindow {
function UpdatePresetStates {
$script:UpdatingPresets = $true
try {
# Build a set of currently checked app tags for fast lookup
$checkedTags = @{}
foreach ($child in $appsPanel.Children) {
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
$checkedTags[$child.Tag] = $true
}
}
# Helper: count matching and checked apps, set checkbox state
function SetPresetState($checkbox, [scriptblock]$MatchFilter) {
$total = 0; $checked = 0
@@ -394,7 +409,7 @@ function Show-MainWindow {
if ($child -is [System.Windows.Controls.CheckBox]) {
if (& $MatchFilter $child) {
$total++
if ($checkedTags.ContainsKey($child.Tag)) { $checked++ }
if ($child.IsChecked) { $checked++ }
}
}
}
@@ -416,12 +431,12 @@ function Show-MainWindow {
SetPresetState $presetDefaultApps { param($c) $c.SelectedByDefault -eq $true }
foreach ($jsonCb in $script:JsonPresetCheckboxes) {
$localIds = $jsonCb.PresetAppIds
SetPresetState $jsonCb { param($c) $localIds -contains $c.Tag }.GetNewClosure()
SetPresetState $jsonCb { param($c) (@($c.AppIds) | Where-Object { $localIds -contains $_ }).Count -gt 0 }.GetNewClosure()
}
# Last used preset: only update if it's visible (has saved apps)
if ($presetLastUsed.Visibility -ne 'Collapsed' -and $script:SavedAppIds) {
SetPresetState $presetLastUsed { param($c) $script:SavedAppIds -contains $c.Tag }
SetPresetState $presetLastUsed { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 }
}
}
finally {
@@ -746,6 +761,9 @@ function Show-MainWindow {
if ($appsToAdd.Count -eq 0) {
$window.FindName('DeploymentApplyBtn').IsEnabled = $true
if ($importConfigBtn) {
$importConfigBtn.IsEnabled = $true
}
return
}
@@ -760,9 +778,9 @@ function Show-MainWindow {
$app = $appsToAdd[$i]
$checkbox = New-Object System.Windows.Controls.CheckBox
$automationName = if ($app.FriendlyName) { $app.FriendlyName } elseif ($app.AppId) { $app.AppId } else { $null }
$automationName = if ($app.FriendlyName) { $app.FriendlyName } elseif ($app.AppIdDisplay) { $app.AppIdDisplay } else { $null }
if ($automationName) { $checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $automationName) }
$checkbox.Tag = $app.AppId
$checkbox.Tag = $app.AppIdDisplay
$checkbox.IsChecked = $app.IsChecked
$checkbox.Style = $window.Resources['AppsPanelCheckBoxStyle']
@@ -798,9 +816,9 @@ function Show-MainWindow {
[System.Windows.Controls.Grid]::SetColumn($tbDesc, 2)
$tbId = New-Object System.Windows.Controls.TextBlock
$tbId.Text = $app.AppId
$tbId.Style = $window.Resources['AppIdTextStyle']
$tbId.ToolTip = $app.AppId
$tbId.Text = $app.AppIdDisplay
$tbId.Style = $window.Resources["AppIdTextStyle"]
$tbId.ToolTip = $app.AppIdDisplay
[System.Windows.Controls.Grid]::SetColumn($tbId, 3)
$row.Children.Add($dot) | Out-Null
@@ -812,6 +830,8 @@ function Show-MainWindow {
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppName' -Value $app.FriendlyName
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppDescription' -Value $app.Description
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'SelectedByDefault' -Value $app.SelectedByDefault
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIds' -Value @($app.AppId)
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIdDisplay' -Value $app.AppIdDisplay
$checkbox.Add_Checked({ UpdateAppSelectionStatus })
$checkbox.Add_Unchecked({ UpdateAppSelectionStatus })
@@ -836,6 +856,9 @@ function Show-MainWindow {
# Re-enable Apply button now that the full, correctly-checked app list is ready
$window.FindName('DeploymentApplyBtn').IsEnabled = $true
if ($importConfigBtn) {
$importConfigBtn.IsEnabled = $true
}
}
# Loads apps into the UI
@@ -844,6 +867,10 @@ function Show-MainWindow {
if ($script:IsLoadingApps) { return }
$script:IsLoadingApps = $true
if ($importConfigBtn) {
$importConfigBtn.IsEnabled = $false
}
# Show loading indicator and clear existing apps
$loadingAppsIndicator.Visibility = 'Visible'
$appsPanel.Children.Clear()
@@ -1575,9 +1602,10 @@ function Show-MainWindow {
$selectedApps = @()
foreach ($child in $appsPanel.Children) {
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
$selectedApps += $child.Tag
$selectedApps += @($child.AppIds)
}
}
$selectedApps = @($selectedApps | Where-Object { $_ } | Select-Object -Unique)
if ($selectedApps.Count -gt 0) {
# Check if Microsoft Store is selected
@@ -1798,7 +1826,7 @@ function Show-MainWindow {
if ($script:UpdatingPresets) { return }
$check = ($this.IsChecked -eq $true)
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
ApplyPresetToApps -MatchFilter { param($c) $script:SavedAppIds -contains $c.Tag } -Check $check
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 } -Check $check
})
}
else {

View File

@@ -152,14 +152,17 @@ function Show-MessageBox {
})
# Show dialog and return result from Tag
$msgWindow.ShowDialog() | Out-Null
# Hide overlay after dialog closes (only if this dialog was the one that showed it)
if ($overlay -and -not $overlayWasAlreadyVisible) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
try {
$msgWindow.ShowDialog() | Out-Null
}
finally {
# Hide overlay after dialog closes (only if this dialog was the one that showed it)
if ($overlay -and -not $overlayWasAlreadyVisible) {
try {
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
}
catch { }
}
catch { }
}
return $msgWindow.Tag

View File

@@ -12,6 +12,7 @@ param (
[switch]$RunDefaults,
[switch]$RunDefaultsLite,
[switch]$RunSavedSettings,
[string]$Config,
[string]$Apps,
[string]$AppRemovalTarget,
[switch]$RemoveApps,

View File

@@ -0,0 +1,127 @@
function ImportConfigToParams {
param (
[Parameter(Mandatory = $true)]
[string]$ConfigPath,
[int]$CurrentBuild,
[string]$ExpectedVersion = '1.0'
)
$resolvedConfigPath = $null
try {
$resolvedConfigPath = (Resolve-Path -LiteralPath $ConfigPath -ErrorAction Stop).Path
}
catch {
throw "Unable to find config file at path: $ConfigPath"
}
if (-not (Test-Path -LiteralPath $resolvedConfigPath -PathType Leaf)) {
throw "Provided config path is not a file: $resolvedConfigPath"
}
if ([System.IO.Path]::GetExtension($resolvedConfigPath) -ne '.json') {
throw "Provided config file must be a .json file: $resolvedConfigPath"
}
$configJson = LoadJsonFile -filePath $resolvedConfigPath -expectedVersion $ExpectedVersion
if ($null -eq $configJson) {
throw "Failed to read config file: $resolvedConfigPath"
}
$importedItems = 0
if ($configJson.Apps) {
$appIds = @(
$configJson.Apps |
Where-Object { $_ -is [string] } |
ForEach-Object { $_.Trim() } |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
)
if ($appIds.Count -gt 0) {
AddParameter 'RemoveApps'
AddParameter 'Apps' ($appIds -join ',')
$importedItems++
}
}
if ($configJson.Tweaks) {
foreach ($setting in @($configJson.Tweaks)) {
if (-not $setting -or -not $setting.Name -or $setting.Value -ne $true) {
continue
}
$feature = $script:Features[$setting.Name]
if (-not $feature) {
continue
}
if (($feature.MinVersion -and $CurrentBuild -lt $feature.MinVersion) -or ($feature.MaxVersion -and $CurrentBuild -gt $feature.MaxVersion) -or ($feature.FeatureId -eq 'DisableModernStandbyNetworking' -and (-not $script:ModernStandbySupported))) {
continue
}
AddParameter $setting.Name $true
$importedItems++
}
}
if ($configJson.Deployment) {
$deploymentLookup = @{}
foreach ($setting in @($configJson.Deployment)) {
if ($setting -and $setting.Name) {
$deploymentLookup[$setting.Name] = $setting.Value
}
}
if ($deploymentLookup.ContainsKey('CreateRestorePoint') -and [bool]$deploymentLookup['CreateRestorePoint']) {
AddParameter 'CreateRestorePoint'
$importedItems++
}
if ($deploymentLookup.ContainsKey('RestartExplorer') -and -not [bool]$deploymentLookup['RestartExplorer']) {
AddParameter 'NoRestartExplorer'
$importedItems++
}
if ($deploymentLookup.ContainsKey('UserSelectionIndex')) {
switch ([int]$deploymentLookup['UserSelectionIndex']) {
1 {
$otherUserName = if ($deploymentLookup.ContainsKey('OtherUsername')) { "$($deploymentLookup['OtherUsername'])".Trim() } else { '' }
if (-not [string]::IsNullOrWhiteSpace($otherUserName)) {
AddParameter 'User' $otherUserName
$importedItems++
}
}
2 {
AddParameter 'Sysprep'
$importedItems++
}
}
}
if ($deploymentLookup.ContainsKey('AppRemovalScopeIndex') -and $script:Params.ContainsKey('RemoveApps')) {
switch ([int]$deploymentLookup['AppRemovalScopeIndex']) {
0 {
AddParameter 'AppRemovalTarget' 'AllUsers'
$importedItems++
}
1 {
AddParameter 'AppRemovalTarget' 'CurrentUser'
$importedItems++
}
2 {
$targetUser = if ($deploymentLookup.ContainsKey('OtherUsername')) { "$($deploymentLookup['OtherUsername'])".Trim() } else { '' }
if (-not [string]::IsNullOrWhiteSpace($targetUser)) {
AddParameter 'AppRemovalTarget' $targetUser
$importedItems++
}
}
}
}
}
if ($importedItems -eq 0) {
throw "The config file contains no importable data: $resolvedConfigPath"
}
return $resolvedConfigPath
}

View File

@@ -14,6 +14,7 @@ param (
[switch]$RunDefaults,
[switch]$RunDefaultsLite,
[switch]$RunSavedSettings,
[string]$Config,
[string]$Apps,
[string]$AppRemovalTarget,
[switch]$RemoveApps,
@@ -121,9 +122,10 @@ $script:ApplyChangesWindowSchema = "$PSScriptRoot/Schemas/ApplyChangesWindow.xam
$script:RevertSettingsWindowSchema = "$PSScriptRoot/Schemas/RevertSettingsWindow.xaml"
$script:SharedStylesSchema = "$PSScriptRoot/Schemas/SharedStyles.xaml"
$script:BubbleHintSchema = "$PSScriptRoot/Schemas/BubbleHint.xaml"
$script:ImportExportConfigSchema = "$PSScriptRoot/Schemas/ImportExportConfigWindow.xaml"
$script:LoadAppsDetailsScriptPath = "$PSScriptRoot/Scripts/FileIO/LoadAppsDetailsFromJson.ps1"
$script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Silent', 'Undo', 'Sysprep', 'User', 'NoRestartExplorer', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'RunAppsListGenerator', 'CLI', 'AppRemovalTarget'
$script:ControlParams = 'WhatIf', 'Confirm', 'Verbose', 'Debug', 'LogPath', 'Silent', 'Undo', 'Sysprep', 'User', 'NoRestartExplorer', 'RunDefaults', 'RunDefaultsLite', 'RunSavedSettings', 'Config', 'RunAppsListGenerator', 'CLI', 'AppRemovalTarget'
# Script-level variables for GUI elements
$script:GuiWindow = $null
@@ -265,6 +267,7 @@ if (-not $script:WingetInstalled -and -not $Silent) {
# File I/O functions
. "$PSScriptRoot/Scripts/FileIO/LoadJsonFile.ps1"
. "$PSScriptRoot/Scripts/FileIO/SaveToFile.ps1"
. "$PSScriptRoot/Scripts/FileIO/SaveSettings.ps1"
. "$PSScriptRoot/Scripts/FileIO/LoadSettings.ps1"
. "$PSScriptRoot/Scripts/FileIO/SaveCustomAppsListToFile.ps1"
@@ -279,6 +282,7 @@ if (-not $script:WingetInstalled -and -not $Silent) {
. "$PSScriptRoot/Scripts/GUI/AttachShiftClickBehavior.ps1"
. "$PSScriptRoot/Scripts/GUI/ApplySettingsToUiControls.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-MessageBox.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-ConfigWindow.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-ApplyModal.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-RevertSettingsModal.ps1"
. "$PSScriptRoot/Scripts/GUI/Show-AppSelectionWindow.ps1"
@@ -292,6 +296,7 @@ if (-not $script:WingetInstalled -and -not $Silent) {
. "$PSScriptRoot/Scripts/Helpers/CheckModernStandbySupport.ps1"
. "$PSScriptRoot/Scripts/Helpers/GenerateAppsList.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetFriendlyTargetUserName.ps1"
. "$PSScriptRoot/Scripts/Helpers/ImportConfigToParams.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetTargetUserForAppRemoval.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetUndoFeatureForParam.ps1"
. "$PSScriptRoot/Scripts/Helpers/GetUserDirectory.ps1"
@@ -413,7 +418,7 @@ if ($RunAppsListGenerator) {
}
# Change script execution based on provided parameters or user input
if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSavedSettings -or ($controlParamsCount -eq $script:Params.Count)) {
if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSavedSettings -or $Config -or ($controlParamsCount -eq $script:Params.Count)) {
if ($RunDefaults -or $RunDefaultsLite) {
ShowCLIDefaultModeOptions
}
@@ -426,6 +431,21 @@ if ((-not $script:Params.Count) -or $RunDefaults -or $RunDefaultsLite -or $RunSa
ShowCLILastUsedSettings
}
elseif ($Config) {
try {
ImportConfigToParams -ConfigPath $Config -CurrentBuild $WinVersion -ExpectedVersion '1.0'
}
catch {
Write-Error "$_"
AwaitKeyToExit
}
if (-not $Silent) {
PrintHeader 'Custom Mode'
PrintPendingChanges
PrintHeader 'Custom Mode'
}
}
else {
if ($launchInCLI) {
$Mode = ShowCLIMenuOptions