# ── 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' Description = 'Notify when the default browser does not match the configured app' Settings = @( @{ Key = 'targetProgId' Label = 'Target ProgId' Type = 'string' Default = 'OperaGXStable' Description = $browserListText } ) } # ── Feature implementation ──────────────────────────────────────────────────── function Invoke-Feature { param( [hashtable]$Config, [hashtable]$State ) if (-not $State) { $State = @{} } if (-not $State.ContainsKey('ignoreUntilDate')) { $State['ignoreUntilDate'] = $null } $today = (Get-Date).ToString('yyyy-MM-dd') $regPath = 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice' try { $currentProgId = (Get-ItemProperty -Path $regPath -Name 'ProgId' -ErrorAction Stop).ProgId } catch { Write-Log -Level Warn ` -Message "Could not read current default browser ProgId from registry: $_" ` -Feature 'DefaultBrowser' return $State } if ($currentProgId -eq $Config['targetProgId']) { Write-Log -Level Info ` -Message "Default browser OK: '$currentProgId'." ` -Feature 'DefaultBrowser' $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 } Write-Log -Level Warn ` -Message "Default browser mismatch — found '$currentProgId', expected '$($Config['targetProgId'])'." ` -Feature 'DefaultBrowser' Show-ToastNotification ` -Title 'Default Browser' ` -Body ("Default browser is '$currentProgId'. Do you want to change it now?") ` -Buttons @( @{ Label = 'Yes'; Action = 'ms-settings:defaultapps' }, @{ Label = 'No'; Action = 'dismiss' } ) $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 }