Enhance user interaction with new dialog features and improve default browser handling
This commit is contained in:
@@ -1,4 +1,105 @@
|
||||
# ── Feature metadata ──────────────────────────────────────────────────────────
|
||||
# ── Browser detection helper ──────────────────────────────────────────────────
|
||||
|
||||
function Get-InstalledBrowsers {
|
||||
$knownBrowsers = @(
|
||||
'ChromeHTML',
|
||||
'FirefoxURL',
|
||||
'OperaGXStable',
|
||||
'Opera GXStable',
|
||||
'SafariHTML',
|
||||
'MSEdgeHTM',
|
||||
'BraveHTML',
|
||||
'Vivaldi',
|
||||
'IEexplore'
|
||||
)
|
||||
|
||||
$installed = @()
|
||||
foreach ($progId in $knownBrowsers) {
|
||||
if (Test-Path "HKLM:\SOFTWARE\Classes\$progId" -ErrorAction SilentlyContinue) {
|
||||
$installed += $progId
|
||||
}
|
||||
}
|
||||
|
||||
return @($installed | Sort-Object)
|
||||
}
|
||||
|
||||
function Get-BrowserSearchText {
|
||||
param([string]$ProgId)
|
||||
|
||||
if (-not $ProgId) { return 'browser' }
|
||||
|
||||
switch -Regex ($ProgId) {
|
||||
'^Opera\s?GXStable$' { return 'opera gx' }
|
||||
'^ChromeHTML$' { return 'chrome' }
|
||||
'^FirefoxURL' { return 'firefox' }
|
||||
'^MSEdgeHTM$' { return 'edge' }
|
||||
'^BraveHTML$' { return 'brave' }
|
||||
'^Vivaldi$' { return 'vivaldi' }
|
||||
default { return $ProgId }
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-DefaultBrowserGuidedChange {
|
||||
param([Parameter(Mandatory)][string]$TargetProgId)
|
||||
|
||||
$regPath = 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice'
|
||||
|
||||
try {
|
||||
$searchText = Get-BrowserSearchText -ProgId $TargetProgId
|
||||
|
||||
Stop-Process -ErrorAction Ignore -Name SystemSettings
|
||||
Start-Process 'ms-settings:defaultapps'
|
||||
|
||||
$ps = Get-Process -ErrorAction Stop SystemSettings
|
||||
do {
|
||||
Start-Sleep -Milliseconds 100
|
||||
$ps.Refresh()
|
||||
} while ([int]$ps.MainWindowHandle -eq 0)
|
||||
|
||||
Start-Sleep -Milliseconds 200
|
||||
|
||||
$shell = New-Object -ComObject WScript.Shell
|
||||
|
||||
foreach ($i in 1..5) {
|
||||
$shell.SendKeys('{TAB}')
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
|
||||
$shell.SendKeys($searchText)
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
$shell.SendKeys('{TAB}')
|
||||
Start-Sleep -Milliseconds 100
|
||||
$shell.SendKeys('{ENTER}')
|
||||
Start-Sleep -Milliseconds 200
|
||||
$shell.SendKeys('{ENTER}')
|
||||
Start-Sleep -Milliseconds 200
|
||||
$shell.SendKeys('%{F4}')
|
||||
Start-Sleep -Milliseconds 300
|
||||
|
||||
$browser = (Get-ItemProperty -Path $regPath -Name 'ProgId' -ErrorAction Stop).ProgId
|
||||
if ($browser -eq $TargetProgId) {
|
||||
Write-Log -Level Info -Message "Default browser successfully changed to '$browser'." -Feature 'DefaultBrowser'
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Log -Level Warn -Message "Browser change attempted, but current ProgId is '$browser' (expected '$TargetProgId')." -Feature 'DefaultBrowser'
|
||||
return $false
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed during guided default-browser change: $_" -Feature 'DefaultBrowser'
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# ── Feature metadata ──────────────────────────────────────────────────────────
|
||||
|
||||
$installedBrowsers = Get-InstalledBrowsers
|
||||
$browserListText = if ($installedBrowsers.Count -gt 0) {
|
||||
"Detected on this system: " + ($installedBrowsers -join ', ')
|
||||
} else {
|
||||
"No known browsers detected. Examples: ChromeHTML, FirefoxURL, OperaGXStable, SafariHTML"
|
||||
}
|
||||
|
||||
$FeatureMeta = @{
|
||||
Name = 'DefaultBrowser'
|
||||
@@ -9,7 +110,7 @@ $FeatureMeta = @{
|
||||
Label = 'Target ProgId'
|
||||
Type = 'string'
|
||||
Default = 'OperaGXStable'
|
||||
Description = 'ProgId of the desired default browser (e.g. OperaGXStable, ChromeHTML, FirefoxURL-308046B0AF4A39CB)'
|
||||
Description = $browserListText
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -23,16 +124,10 @@ function Invoke-Feature {
|
||||
)
|
||||
|
||||
if (-not $State) { $State = @{} }
|
||||
if (-not $State.ContainsKey('lastShownDate')) { $State['lastShownDate'] = $null }
|
||||
if (-not $State.ContainsKey('ignoreUntilDate')) { $State['ignoreUntilDate'] = $null }
|
||||
|
||||
$today = (Get-Date).ToString('yyyy-MM-dd')
|
||||
|
||||
# Only notify once per day
|
||||
if ($State['lastShownDate'] -eq $today) {
|
||||
Write-Log -Level Info -Message 'Already checked today, skipping.' -Feature 'DefaultBrowser'
|
||||
return $State
|
||||
}
|
||||
|
||||
$regPath = 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice'
|
||||
|
||||
try {
|
||||
@@ -49,7 +144,14 @@ function Invoke-Feature {
|
||||
Write-Log -Level Info `
|
||||
-Message "Default browser OK: '$currentProgId'." `
|
||||
-Feature 'DefaultBrowser'
|
||||
$State['lastShownDate'] = $today
|
||||
$State['ignoreUntilDate'] = $null
|
||||
return $State
|
||||
}
|
||||
|
||||
if ($State['ignoreUntilDate'] -eq $today) {
|
||||
Write-Log -Level Info `
|
||||
-Message "Mismatch ignored for today (current '$currentProgId', expected '$($Config['targetProgId'])')." `
|
||||
-Feature 'DefaultBrowser'
|
||||
return $State
|
||||
}
|
||||
|
||||
@@ -57,17 +159,29 @@ function Invoke-Feature {
|
||||
-Message "Default browser mismatch — found '$currentProgId', expected '$($Config['targetProgId'])'." `
|
||||
-Feature 'DefaultBrowser'
|
||||
|
||||
# Note: Windows 11 blocks programmatic default-browser changes via registry hash protection.
|
||||
# We guide the user to the Settings page instead.
|
||||
Show-ToastNotification `
|
||||
-Title 'Default Browser' `
|
||||
-Body ("Default browser is '$currentProgId'. Click below to set it to $($Config['targetProgId']).") `
|
||||
-Body ("Default browser is '$currentProgId'. Do you want to change it now?") `
|
||||
-Buttons @(
|
||||
@{ Label = 'Open Default Apps Settings'; Action = 'ms-settings:defaultapps' },
|
||||
@{ Label = 'Dismiss'; Action = 'dismiss' }
|
||||
@{ Label = 'Yes'; Action = 'ms-settings:defaultapps' },
|
||||
@{ Label = 'No'; Action = 'dismiss' }
|
||||
)
|
||||
|
||||
$State['lastShownDate'] = $today
|
||||
$decision = Show-ConfirmationDialog `
|
||||
-Title 'Default Browser' `
|
||||
-Message ("Default browser is '$currentProgId', expected '$($Config['targetProgId'])'.`n`nDo you want to change it now?") `
|
||||
-Feature 'DefaultBrowser' `
|
||||
-Default 'No'
|
||||
|
||||
if ($decision -eq 'Yes') {
|
||||
[void](Invoke-DefaultBrowserGuidedChange -TargetProgId $Config['targetProgId'])
|
||||
$State['ignoreUntilDate'] = $null
|
||||
}
|
||||
else {
|
||||
$State['ignoreUntilDate'] = $today
|
||||
Write-Log -Level Info -Message 'User selected No; mismatch notifications suppressed until tomorrow.' -Feature 'DefaultBrowser'
|
||||
}
|
||||
|
||||
return $State
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ function Invoke-Feature {
|
||||
|
||||
if (-not $State) { $State = @{} }
|
||||
if (-not $State.ContainsKey('lastShownDate')) { $State['lastShownDate'] = $null }
|
||||
if (-not $State.ContainsKey('snoozeUntil')) { $State['snoozeUntil'] = $null }
|
||||
|
||||
$today = (Get-Date).ToString('yyyy-MM-dd')
|
||||
|
||||
@@ -54,6 +55,21 @@ function Invoke-Feature {
|
||||
return $State
|
||||
}
|
||||
|
||||
if ($State['snoozeUntil']) {
|
||||
try {
|
||||
$snoozeUntil = [datetime]::Parse($State['snoozeUntil'])
|
||||
if ((Get-Date) -lt $snoozeUntil) {
|
||||
Write-Log -Level Info `
|
||||
-Message ("Snoozed until {0}, skipping." -f $snoozeUntil.ToString('HH:mm:ss')) `
|
||||
-Feature 'SandwichReminder'
|
||||
return $State
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Warn -Message "Invalid snoozeUntil value '$($State['snoozeUntil'])', ignoring." -Feature 'SandwichReminder'
|
||||
}
|
||||
}
|
||||
|
||||
# Parse reminder time
|
||||
try {
|
||||
$targetTime = [datetime]::ParseExact($Config['reminderTime'], 'HH:mm', $null)
|
||||
@@ -86,17 +102,48 @@ function Invoke-Feature {
|
||||
return $State
|
||||
}
|
||||
|
||||
Write-Log -Level Info -Message 'Showing sandwich reminder toast.' -Feature 'SandwichReminder'
|
||||
Write-Log -Level Info -Message 'Showing sandwich reminder confirmation dialog.' -Feature 'SandwichReminder'
|
||||
|
||||
Show-ToastNotification `
|
||||
-Title 'Sandwich Order' `
|
||||
-Body 'Do you want to order a sandwich today?' `
|
||||
-Buttons @(
|
||||
@{ Label = 'Yes, order now!'; Action = $Config['url'] },
|
||||
@{ Label = 'No thanks'; Action = 'dismiss' }
|
||||
$decision = Show-MultiChoiceDialog `
|
||||
-Title 'Sandwich Order' `
|
||||
-Message 'Do you want to order a sandwich today?' `
|
||||
-Feature 'SandwichReminder' `
|
||||
-DefaultValue 'snooze15' `
|
||||
-Choices @(
|
||||
@{ Label = 'Order now'; Value = 'order' },
|
||||
@{ Label = 'Snooze 15 min'; Value = 'snooze15' },
|
||||
@{ Label = 'Snooze 1h'; Value = 'snooze60' },
|
||||
@{ Label = 'No'; Value = 'no' }
|
||||
)
|
||||
|
||||
$State['lastShownDate'] = $today
|
||||
if ($decision -eq 'order') {
|
||||
try {
|
||||
Start-Process $Config['url']
|
||||
Write-Log -Level Info -Message "Opened sandwich order URL: $($Config['url'])" -Feature 'SandwichReminder'
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to open sandwich order URL '$($Config['url'])': $_" -Feature 'SandwichReminder'
|
||||
}
|
||||
|
||||
$State['lastShownDate'] = $today
|
||||
$State['snoozeUntil'] = $null
|
||||
}
|
||||
elseif ($decision -eq 'snooze60') {
|
||||
$until = (Get-Date).AddHours(1)
|
||||
$State['snoozeUntil'] = $until.ToString('o')
|
||||
Write-Log -Level Info -Message ("User snoozed sandwich reminder for 1 hour (until {0})." -f $until.ToString('HH:mm:ss')) -Feature 'SandwichReminder'
|
||||
}
|
||||
elseif ($decision -eq 'no') {
|
||||
$State['lastShownDate'] = $today
|
||||
$State['snoozeUntil'] = $null
|
||||
Write-Log -Level Info -Message 'User selected No for sandwich reminder; skipping for rest of day.' -Feature 'SandwichReminder'
|
||||
}
|
||||
else {
|
||||
$until = (Get-Date).AddMinutes(15)
|
||||
$State['snoozeUntil'] = $until.ToString('o')
|
||||
Write-Log -Level Info -Message ("User snoozed sandwich reminder for 15 minutes (until {0})." -f $until.ToString('HH:mm:ss')) -Feature 'SandwichReminder'
|
||||
}
|
||||
|
||||
return $State
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# PromptHelper.ps1 — shared foreground/system-modal confirmation dialog helper.
|
||||
|
||||
function Show-ConfirmationDialog {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Title,
|
||||
[Parameter(Mandatory)][string]$Message,
|
||||
[string]$Feature = 'PromptHelper',
|
||||
[string]$Default = 'No'
|
||||
)
|
||||
|
||||
try {
|
||||
$shell = New-Object -ComObject WScript.Shell
|
||||
# 4=YesNo, 32=Question icon, 4096=System modal, 65536=Foreground
|
||||
$result = $shell.Popup($Message, 0, $Title, 4 + 32 + 4096 + 65536)
|
||||
|
||||
switch ($result) {
|
||||
6 { return 'Yes' }
|
||||
7 { return 'No' }
|
||||
default { return $Default }
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to show confirmation dialog '$Title': $_" -Feature $Feature
|
||||
return $Default
|
||||
}
|
||||
}
|
||||
|
||||
function Show-MultiChoiceDialog {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Title,
|
||||
[Parameter(Mandatory)][string]$Message,
|
||||
[Parameter(Mandatory)][object[]]$Choices,
|
||||
[string]$Feature = 'PromptHelper',
|
||||
[string]$DefaultValue = ''
|
||||
)
|
||||
|
||||
try {
|
||||
Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue | Out-Null
|
||||
Add-Type -AssemblyName System.Drawing -ErrorAction SilentlyContinue | Out-Null
|
||||
|
||||
if ($Choices.Count -lt 1 -or $Choices.Count -gt 4) {
|
||||
throw 'Show-MultiChoiceDialog expects 1 to 4 choices.'
|
||||
}
|
||||
|
||||
$form = New-Object System.Windows.Forms.Form
|
||||
$form.Text = $Title
|
||||
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
|
||||
$form.TopMost = $true
|
||||
$form.Width = 480
|
||||
$form.Height = 190
|
||||
$form.MinimizeBox = $false
|
||||
$form.MaximizeBox = $false
|
||||
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
|
||||
|
||||
$label = New-Object System.Windows.Forms.Label
|
||||
$label.AutoSize = $false
|
||||
$label.Left = 12
|
||||
$label.Top = 12
|
||||
$label.Width = 440
|
||||
$label.Height = 80
|
||||
$label.Text = $Message
|
||||
$form.Controls.Add($label)
|
||||
|
||||
$form.Tag = $DefaultValue
|
||||
$gap = 10
|
||||
$availableWidth = $form.ClientSize.Width - 24 - ($gap * ($Choices.Count - 1))
|
||||
$buttonWidth = [Math]::Min(130, [Math]::Floor($availableWidth / $Choices.Count))
|
||||
$totalWidth = ($buttonWidth * $Choices.Count) + ($gap * ($Choices.Count - 1))
|
||||
$startX = [Math]::Max(12, [int](($form.ClientSize.Width - $totalWidth) / 2))
|
||||
|
||||
for ($i = 0; $i -lt $Choices.Count; $i++) {
|
||||
$choice = $Choices[$i]
|
||||
$button = New-Object System.Windows.Forms.Button
|
||||
$button.Left = $startX + ($i * ($buttonWidth + $gap))
|
||||
$button.Top = 105
|
||||
$button.Width = $buttonWidth
|
||||
$button.Height = 30
|
||||
$button.Text = [string]$choice.Label
|
||||
$button.Tag = [string]$choice.Value
|
||||
$button.Add_Click({
|
||||
param($sender, $eventArgs)
|
||||
$dlg = $sender.FindForm()
|
||||
$dlg.Tag = [string]$sender.Tag
|
||||
$dlg.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
||||
$dlg.Close()
|
||||
})
|
||||
$form.Controls.Add($button)
|
||||
if ($i -eq 0) {
|
||||
$form.AcceptButton = $button
|
||||
}
|
||||
}
|
||||
|
||||
[void]$form.ShowDialog()
|
||||
$result = [string]$form.Tag
|
||||
$form.Dispose()
|
||||
|
||||
return $result
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to show multi-choice dialog '$Title': $_" -Feature $Feature
|
||||
return $DefaultValue
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ $script:InternalRoot = $PSScriptRoot # runner.ps1 lives in internal\
|
||||
. (Join-Path $InternalRoot 'lib\Logging.ps1')
|
||||
. (Join-Path $InternalRoot 'lib\NetworkUtils.ps1')
|
||||
. (Join-Path $InternalRoot 'lib\ToastHelper.ps1')
|
||||
. (Join-Path $InternalRoot 'lib\PromptHelper.ps1')
|
||||
. (Join-Path $InternalRoot 'lib\Config.ps1')
|
||||
|
||||
Write-Log -Level Info -Message '────── Runner started ──────' -Feature 'Runner'
|
||||
|
||||
Reference in New Issue
Block a user