From e9a917354f61e047ddd1a923f549870a8893f2d8 Mon Sep 17 00:00:00 2001 From: Elliot Date: Mon, 14 Sep 2026 10:55:10 +0800 Subject: [PATCH 1/4] feat: pass WSL distro identity to notifications --- home/.config/sheldon/plugins/wsl-notify.plugin.zsh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/home/.config/sheldon/plugins/wsl-notify.plugin.zsh b/home/.config/sheldon/plugins/wsl-notify.plugin.zsh index a1e0955c..6447ff81 100644 --- a/home/.config/sheldon/plugins/wsl-notify.plugin.zsh +++ b/home/.config/sheldon/plugins/wsl-notify.plugin.zsh @@ -49,7 +49,8 @@ function bgnotify { if powershell.exe -NoLogo -NoProfile -NonInteractive \ -ExecutionPolicy Bypass -File "$windows_notify_script" \ - -Title "$title" -Message "$message" >/dev/null 2>&1; then + -Title "$title" -Message "$message" \ + -DistroName "${WSL_DISTRO_NAME:-}" >/dev/null 2>&1; then notify_status=0 else notify_status=$? From fb265d45f8bd3565478a73d47b36fcf1c2377b4f Mon Sep 17 00:00:00 2001 From: Elliot Date: Mon, 14 Sep 2026 10:55:55 +0800 Subject: [PATCH 2/4] feat: show current WSL distro icon in toasts --- config/scripts/wsl-notify.ps1 | 139 +++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/config/scripts/wsl-notify.ps1 b/config/scripts/wsl-notify.ps1 index 3be5eeb6..5efb6198 100644 --- a/config/scripts/wsl-notify.ps1 +++ b/config/scripts/wsl-notify.ps1 @@ -1,6 +1,7 @@ param( [Parameter(Mandatory = $true)][string] $Title, - [Parameter(Mandatory = $true)][string] $Message + [Parameter(Mandatory = $true)][string] $Message, + [string] $DistroName = '' ) $ErrorActionPreference = 'Stop' @@ -74,6 +75,130 @@ function Get-NotificationAppId { Select-Object -First 1 -ExpandProperty AppID } +function Get-WslDistroIconPath { + param([string] $Name) + + if ([string]::IsNullOrWhiteSpace($Name)) { + return $null + } + + foreach ($root in @(\\"\\wsl.localhost\\$Name\\", \\"\\wsl`$\\$Name\\")) { + $configPath = Join-Path -Path $root -ChildPath 'etc\wsl-distribution.conf' + if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) { + continue + } + + try { + $section = '' + $linuxIconPath = $null + + foreach ($rawLine in Get-Content -LiteralPath $configPath -ErrorAction Stop) { + $line = ($rawLine -split '[#;]', 2)[0].Trim() + if ($line.Length -eq 0) { + continue + } + + if ($line -match '^\[(?
[^\]]+)\]$') { + $section = $Matches['Section'].Trim() + continue + } + + if ($section -ieq 'shortcut' -and $line -match '^(?i:icon)\s*=\s*(?.+)$') { + $linuxIconPath = $Matches['Icon'].Trim() + if (($linuxIconPath.StartsWith('"') -and $linuxIconPath.EndsWith('"')) -or + ($linuxIconPath.StartsWith("'") -and $linuxIconPath.EndsWith("'"))) { + $linuxIconPath = $linuxIconPath.Substring(1, $linuxIconPath.Length - 2) + } + break + } + } + + if ([string]::IsNullOrWhiteSpace($linuxIconPath) -or -not $linuxIconPath.StartsWith('/')) { + continue + } + + $relativePath = $linuxIconPath.TrimStart('/').Replace('/', '\') + $candidate = Join-Path -Path $root -ChildPath $relativePath + if (Test-Path -LiteralPath $candidate -PathType Leaf) { + return $candidate + } + } + catch { + continue + } + } + + return $null +} + +function Get-WslDistroIconUri { + param([string] $Name) + + $sourcePath = Get-WslDistroIconPath -Name $Name + if ([string]::IsNullOrWhiteSpace($sourcePath)) { + return $null + } + + $extension = [IO.Path]::GetExtension($sourcePath).ToLowerInvariant() + if ($extension -in @('.png', '.jpg', '.jpeg', '.svg')) { + return ([Uri] $sourcePath).AbsoluteUri + } + + if ($extension -ne '.ico') { + return $null + } + + try { + Add-Type -AssemblyName System.Drawing + + $localAppData = [Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData) + if ([string]::IsNullOrWhiteSpace($localAppData)) { + return $null + } + + $cacheDirectory = Join-Path -Path $localAppData -ChildPath 'wsl-notify\icons' + [void] [IO.Directory]::CreateDirectory($cacheDirectory) + + $sha256 = [Security.Cryptography.SHA256]::Create() + try { + $cacheKey = [Text.Encoding]::UTF8.GetBytes("$Name`0$sourcePath") + $hash = [BitConverter]::ToString($sha256.ComputeHash($cacheKey)).Replace('-', '').ToLowerInvariant() + } + finally { + $sha256.Dispose() + } + + $cachePath = Join-Path -Path $cacheDirectory -ChildPath "$hash.png" + $sourceInfo = Get-Item -LiteralPath $sourcePath -ErrorAction Stop + $refreshCache = -not (Test-Path -LiteralPath $cachePath -PathType Leaf) + if (-not $refreshCache) { + $cacheInfo = Get-Item -LiteralPath $cachePath -ErrorAction Stop + $refreshCache = $cacheInfo.LastWriteTimeUtc -lt $sourceInfo.LastWriteTimeUtc + } + + if ($refreshCache) { + $icon = New-Object -TypeName System.Drawing.Icon -ArgumentList @($sourcePath, 48, 48) + try { + $bitmap = $icon.ToBitmap() + try { + $bitmap.Save($cachePath, [System.Drawing.Imaging.ImageFormat]::Png) + } + finally { + $bitmap.Dispose() + } + } + finally { + $icon.Dispose() + } + } + + return ([Uri] $cachePath).AbsoluteUri + } + catch { + return $null + } +} + $foregroundProcess = Get-ForegroundProcessName if ([string]::IsNullOrWhiteSpace($foregroundProcess)) { exit $ExitFocusUnknown @@ -100,6 +225,18 @@ try { [void] $textNodes.Item(0).AppendChild($document.CreateTextNode($Title)) [void] $textNodes.Item(1).AppendChild($document.CreateTextNode($Message)) + $iconUri = Get-WslDistroIconUri -Name $DistroName + if (-not [string]::IsNullOrWhiteSpace($iconUri)) { + $binding = $document.SelectSingleNode('/toast/visual/binding') + $image = $document.CreateElement('image') + $image.SetAttribute('placement', 'appLogoOverride') + $image.SetAttribute('src', $iconUri) + if (-not [string]::IsNullOrWhiteSpace($DistroName)) { + $image.SetAttribute('alt', $DistroName) + } + [void] $binding.AppendChild($image) + } + $toast = [Windows.UI.Notifications.ToastNotification]::new($document) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($appId).Show($toast) exit $ExitToastShown From daf53452ab14109ddc60e15e4c618774221da86b Mon Sep 17 00:00:00 2001 From: Elliot Date: Mon, 14 Sep 2026 10:57:36 +0800 Subject: [PATCH 3/4] fix: resolve WSL distro icon paths correctly --- config/scripts/wsl-notify.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/scripts/wsl-notify.ps1 b/config/scripts/wsl-notify.ps1 index 5efb6198..2fb5e485 100644 --- a/config/scripts/wsl-notify.ps1 +++ b/config/scripts/wsl-notify.ps1 @@ -82,7 +82,12 @@ function Get-WslDistroIconPath { return $null } - foreach ($root in @(\\"\\wsl.localhost\\$Name\\", \\"\\wsl`$\\$Name\\")) { + $roots = @( + ('\\wsl.localhost\{0}' -f $Name), + ('\\wsl$\{0}' -f $Name) + ) + + foreach ($root in $roots) { $configPath = Join-Path -Path $root -ChildPath 'etc\wsl-distribution.conf' if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) { continue From 739180fe0189dab92f1132907686f6362fd27427 Mon Sep 17 00:00:00 2001 From: Elliot Date: Mon, 14 Sep 2026 10:57:48 +0800 Subject: [PATCH 4/4] test: verify WSL distro name reaches toast helper --- config/tests/wsl-notify.zsh | 1 + 1 file changed, 1 insertion(+) diff --git a/config/tests/wsl-notify.zsh b/config/tests/wsl-notify.zsh index eb141ee3..d92e0cbf 100644 --- a/config/tests/wsl-notify.zsh +++ b/config/tests/wsl-notify.zsh @@ -44,6 +44,7 @@ export WSL_NOTIFY_TEST_EXIT=20 output=$(bgnotify 'build finished' 'command completed' '') [[ "$output" == $'\a' ]] [[ "$(wc -l <"$WSL_NOTIFY_TEST_LOG")" -eq 4 ]] +[[ "$(grep -c -- '-DistroName ci' "$WSL_NOTIFY_TEST_LOG")" -eq 4 ]] # Exercise the Ghostty hook-removal path without depending on the runner's zsh # function installation. The fixture is loaded through zsh's real autoload