diff --git a/tools/autoclaw-blue-branding/tray_icon.ico b/tools/autoclaw-blue-branding/tray_icon.ico index 83addfe..05741da 100644 Binary files a/tools/autoclaw-blue-branding/tray_icon.ico and b/tools/autoclaw-blue-branding/tray_icon.ico differ diff --git a/tools/autoclaw-green-branding/tray_icon.ico b/tools/autoclaw-green-branding/tray_icon.ico index 5d8a446..06d2f33 100644 Binary files a/tools/autoclaw-green-branding/tray_icon.ico and b/tools/autoclaw-green-branding/tray_icon.ico differ diff --git a/tools/autoclaw-red-branding/tray_icon.ico b/tools/autoclaw-red-branding/tray_icon.ico index 4e1a866..2c74f3b 100644 Binary files a/tools/autoclaw-red-branding/tray_icon.ico and b/tools/autoclaw-red-branding/tray_icon.ico differ diff --git a/tools/autoclaw-yellow-branding/tray_icon.ico b/tools/autoclaw-yellow-branding/tray_icon.ico index 1d5c1d4..dbba86a 100644 Binary files a/tools/autoclaw-yellow-branding/tray_icon.ico and b/tools/autoclaw-yellow-branding/tray_icon.ico differ diff --git a/tools/autoclaw2-blue-branding/tray_icon.ico b/tools/autoclaw2-blue-branding/tray_icon.ico index 8de0613..e329438 100644 Binary files a/tools/autoclaw2-blue-branding/tray_icon.ico and b/tools/autoclaw2-blue-branding/tray_icon.ico differ diff --git a/tools/autoclaw2-green-branding/tray_icon.ico b/tools/autoclaw2-green-branding/tray_icon.ico index c0a7de8..c9c2843 100644 Binary files a/tools/autoclaw2-green-branding/tray_icon.ico and b/tools/autoclaw2-green-branding/tray_icon.ico differ diff --git a/tools/autoclaw2-orange-branding/tray_icon.ico b/tools/autoclaw2-orange-branding/tray_icon.ico index 697c0d0..841a2a0 100644 Binary files a/tools/autoclaw2-orange-branding/tray_icon.ico and b/tools/autoclaw2-orange-branding/tray_icon.ico differ diff --git a/tools/autoclaw2-red-branding/tray_icon.ico b/tools/autoclaw2-red-branding/tray_icon.ico index 9e17531..6b90f78 100644 Binary files a/tools/autoclaw2-red-branding/tray_icon.ico and b/tools/autoclaw2-red-branding/tray_icon.ico differ diff --git a/tools/autoclaw2-yellow-branding/tray_icon.ico b/tools/autoclaw2-yellow-branding/tray_icon.ico index 7a08650..15db433 100644 Binary files a/tools/autoclaw2-yellow-branding/tray_icon.ico and b/tools/autoclaw2-yellow-branding/tray_icon.ico differ diff --git a/tools/cline-blue-branding/tray_icon.ico b/tools/cline-blue-branding/tray_icon.ico index 8abea91..651995b 100644 Binary files a/tools/cline-blue-branding/tray_icon.ico and b/tools/cline-blue-branding/tray_icon.ico differ diff --git a/tools/cline-green-branding/tray_icon.ico b/tools/cline-green-branding/tray_icon.ico index ddeef77..1a1b6bc 100644 Binary files a/tools/cline-green-branding/tray_icon.ico and b/tools/cline-green-branding/tray_icon.ico differ diff --git a/tools/cline-orange-branding/tray_icon.ico b/tools/cline-orange-branding/tray_icon.ico index 4e02a50..e41e637 100644 Binary files a/tools/cline-orange-branding/tray_icon.ico and b/tools/cline-orange-branding/tray_icon.ico differ diff --git a/tools/cline-red-branding/tray_icon.ico b/tools/cline-red-branding/tray_icon.ico index 3bd6f5d..e33a20b 100644 Binary files a/tools/cline-red-branding/tray_icon.ico and b/tools/cline-red-branding/tray_icon.ico differ diff --git a/tools/cline-yellow-branding/tray_icon.ico b/tools/cline-yellow-branding/tray_icon.ico index 50b742a..13fd903 100644 Binary files a/tools/cline-yellow-branding/tray_icon.ico and b/tools/cline-yellow-branding/tray_icon.ico differ diff --git a/tools/install_autoclaw2_clone_shortcuts.ps1 b/tools/install_autoclaw2_clone_shortcuts.ps1 index 936981c..083793a 100644 --- a/tools/install_autoclaw2_clone_shortcuts.ps1 +++ b/tools/install_autoclaw2_clone_shortcuts.ps1 @@ -52,6 +52,15 @@ $ErrorActionPreference = 'Stop' $scriptPath = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Definition } $RepoRoot = Split-Path -Parent $scriptPath +# A relative $RepoRoot (bare-filename $MyInvocation fallback) once leaked a +# RELATIVE path into the .lnk IconLocation - Windows cannot resolve a relative +# icon path, so every shortcut rendered BLANK (live bug found 2026-10-29 on +# the Desktop/Start Menu/taskbar icons). Resolve against the current location +# and refuse to continue when the root is not absolute. +$RepoRoot = (Resolve-Path $RepoRoot).ProviderPath +if (-not [System.IO.Path]::IsPathRooted($RepoRoot)) { + throw "repo root resolved non-absolute: '$RepoRoot' - run the installer against the real repo folder" +} $Launcher = Join-Path $RepoRoot 'src\windows\launch_autoclaw2_clone.ps1' $Desktop = [Environment]::GetFolderPath('Desktop') $StartMenu = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs' @@ -198,11 +207,17 @@ foreach ($id in $TargetIds) { $chk = $sh.CreateShortcut($lnk) $aumidBack = Get-ShortcutAumid $lnk + # Verify-after covers the icon too: "$path,index" -> the path must be + # ABSOLUTE and exist (a relative IconLocation renders a blank shortcut + # icon - the live bug this suite shipped with, fixed 2026-10-29). + $iconBack = ($chk.IconLocation -replace ',\d+$', '') + $iconOk = ($iconBack -ieq $iconPath) -and [System.IO.Path]::IsPathRooted($iconBack) -and (Test-Path -LiteralPath $iconBack) $ok = ($chk.TargetPath -eq 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe') -and ($chk.Arguments -match "launch_autoclaw2_clone\.ps1.*-Instance $id") -and - ($aumidBack -eq $aumid) + ($aumidBack -eq $aumid) -and + $iconOk if (-not $ok) { $script:Failed = $true } - Write-Host ("{0} : {1} aumid={2}" -f $(if ($ok) {'OK '} else {'FAIL'}), $lnk, $(if ($aumidBack) { $aumidBack } else { '' })) + Write-Host ("{0} : {1} aumid={2} icon={3}" -f $(if ($ok) {'OK '} else {'FAIL'}), $lnk, $(if ($aumidBack) { $aumidBack } else { '' }), $(if ($iconOk) { $iconBack } else { "BAD: $($chk.IconLocation)" })) } } diff --git a/tools/install_cline_clone_shortcuts.ps1 b/tools/install_cline_clone_shortcuts.ps1 index a5359ea..d7a0025 100644 --- a/tools/install_cline_clone_shortcuts.ps1 +++ b/tools/install_cline_clone_shortcuts.ps1 @@ -50,6 +50,15 @@ $ErrorActionPreference = 'Stop' $scriptPath = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Definition } $RepoRoot = Split-Path -Parent $scriptPath +# A relative $RepoRoot (bare-filename $MyInvocation fallback) once leaked a +# RELATIVE path into the .lnk IconLocation - Windows cannot resolve a relative +# icon path, so every shortcut rendered BLANK (live bug found 2026-10-29 on +# the Desktop/Start Menu/taskbar icons). Resolve against the current location +# and refuse to continue when the root is not absolute. +$RepoRoot = (Resolve-Path $RepoRoot).ProviderPath +if (-not [System.IO.Path]::IsPathRooted($RepoRoot)) { + throw "repo root resolved non-absolute: '$RepoRoot' - run the installer against the real repo folder" +} $Launcher = Join-Path $RepoRoot 'src\windows\launch_cline_clone.ps1' $ExePath = Join-Path $env:LOCALAPPDATA 'Cline\cline-app.exe' $Desktop = [Environment]::GetFolderPath('Desktop') @@ -196,10 +205,16 @@ foreach ($id in $TargetIds) { try { Set-ShortcutAumid -LnkPath $lnk -Aumid $aumid } catch { Write-Warning "AUMID set failed on $lnk : $($_.Exception.Message)" } $chk = $sh.CreateShortcut($lnk) + # Verify-after covers the icon too: "$path,index" -> the path must be + # ABSOLUTE and exist (a relative IconLocation renders a blank shortcut + # icon - the live bug this suite shipped with, fixed 2026-10-29). + $iconBack = ($chk.IconLocation -replace ',\d+$', '') + $iconOk = ($iconBack -ieq $iconPath) -and [System.IO.Path]::IsPathRooted($iconBack) -and (Test-Path -LiteralPath $iconBack) $ok = ($chk.TargetPath -eq 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe') -and - ($chk.Arguments -match "launch_cline_clone\.ps1.*-Instance $id") + ($chk.Arguments -match "launch_cline_clone\.ps1.*-Instance $id") -and + $iconOk $aumidBack = Get-ShortcutAumid $lnk - Write-Host ("{0} : {1} aumid={2}" -f $(if ($ok) {'OK '} else {'FAIL'}), $lnk, $(if ($aumidBack) { $aumidBack } else { '' })) + Write-Host ("{0} : {1} aumid={2} icon={3}" -f $(if ($ok) {'OK '} else {'FAIL'}), $lnk, $(if ($aumidBack) { $aumidBack } else { '' }), $(if ($iconOk) { $iconBack } else { "BAD: $($chk.IconLocation)" })) } } diff --git a/tools/make_clone_icon.ps1 b/tools/make_clone_icon.ps1 index b22325d..3f3e469 100644 --- a/tools/make_clone_icon.ps1 +++ b/tools/make_clone_icon.ps1 @@ -16,7 +16,12 @@ (-EdgeInsetPercent, drops the soft drop-shadow halo) -> high-quality resample to a 1024 canvas -> optional -HueRotate (degrees; for derived clone colors such as a 4th color) -> anti-aliased rounded-corner alpha mask (-CornerRadiusPercent) -> - PNG-compressed ICO entries for every size in -Sizes (16..256). + 32-bit BMP (BGRA DIB) ICO entries for every size below 256 plus a PNG entry + for 256 - the only layout the Windows shell renders reliably. PNG frames are + officially valid at 256 ONLY: small PNG frames decode as noise or blank in + Explorer and System.Drawing (live bug found 2026-10-29 in EVERY suite this + generator produced - blank Desktop/Start Menu/taskbar shortcut icons; the + zcode ICOs were always correct because make_ghost_icon.ps1 writes DIBs). Re-runs without -Source reuse the saved icon_windows.png when its corners are transparent (already-masked master) - regeneration never depends on Downloads. @@ -446,11 +451,51 @@ function Convert-MasterToPngBytes { finally { $out.Dispose() } } +# BMP (BGRA DIB) frame for every size below 256: BITMAPINFOHEADER + bottom-up +# BGRA rows + an all-zero AND mask (the alpha channel in the XOR plane governs). +# This is the frame format make_ghost_icon.ps1 has always written and the only +# one Explorer renders at small sizes. +function Convert-MasterToDibBytes { + param([System.Drawing.Bitmap]$M, [int]$S) + $out = New-Object System.Drawing.Bitmap ($S), ($S) + try { + $g = [System.Drawing.Graphics]::FromImage($out) + $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic + $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality + $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality + $g.DrawImage($M, 0, 0, $S, $S) + $g.Dispose() + $rect = New-Object System.Drawing.Rectangle 0, 0, $S, $S + $data = $out.LockBits($rect, [System.Drawing.Imaging.ImageLockMode]::ReadOnly, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb) + $stride = $data.Stride + $px = New-Object byte[] ($stride * $S) + [System.Runtime.InteropServices.Marshal]::Copy($data.Scan0, $px, 0, $px.Length) + $out.UnlockBits($data) + $ms = New-Object System.IO.MemoryStream + $bw2 = New-Object System.IO.BinaryWriter($ms) + $bw2.Write([uint32]40); $bw2.Write([int32]$S); $bw2.Write([int32]($S * 2)) + $bw2.Write([uint16]1); $bw2.Write([uint16]32) + $bw2.Write([uint32]0); $bw2.Write([uint32]($S * $S * 4)) + $bw2.Write([int32]0); $bw2.Write([int32]0); $bw2.Write([uint32]0); $bw2.Write([uint32]0) + for ($y = $S - 1; $y -ge 0; $y--) { $bw2.Write($px, $y * $stride, $S * 4) } + $maskStride = [int]([Math]::Ceiling([Math]::Ceiling($S / 8.0) / 4.0) * 4) + $bw2.Write((New-Object byte[] ($maskStride * $S))) + $bw2.Flush() + return ,$ms.ToArray() + } + finally { $out.Dispose() } +} + # --------------------------------------------------------------- write the .ico Write-Host '[4/5] Writing ICO container...' $sorted = @($Sizes | Sort-Object) -$pngs = @() -foreach ($s in $sorted) { $pngs += ,(Convert-MasterToPngBytes -M $master -S $s) } +$frames = @() +foreach ($s in $sorted) { + # 256 is the one PNG-legal size; every smaller frame must be a BMP DIB or + # Explorer renders the shortcut icon blank (live bug 2026-10-29). + if ($s -ge 256) { $frames += ,(Convert-MasterToPngBytes -M $master -S $s) } + else { $frames += ,(Convert-MasterToDibBytes -M $master -S $s) } +} $icoMs = New-Object System.IO.MemoryStream $bw = New-Object System.IO.BinaryWriter($icoMs) @@ -464,11 +509,11 @@ for ($i = 0; $i -lt $sorted.Count; $i++) { $bw.Write([byte]$dim); $bw.Write([byte]$dim) $bw.Write([byte]0); $bw.Write([byte]0) # palette, reserved $bw.Write([uint16]1); $bw.Write([uint16]32) # planes, bpp - $bw.Write([uint32]$pngs[$i].Length) + $bw.Write([uint32]$frames[$i].Length) $bw.Write([uint32]$offset) - $offset += $pngs[$i].Length + $offset += $frames[$i].Length } -foreach ($p in $pngs) { $bw.Write($p) } +foreach ($p in $frames) { $bw.Write($p) } $bw.Flush() [System.IO.File]::WriteAllBytes($IcoPath, $icoMs.ToArray()) $bw.Dispose(); $icoMs.Dispose() @@ -488,6 +533,10 @@ for ($i = 0; $i -lt $vCount; $i++) { $len = [BitConverter]::ToUInt32($check, 6 + 16 * $i + 8) $off = [BitConverter]::ToUInt32($check, 6 + 16 * $i + 12) if ($off + $len -gt $check.Length) { throw ("FAILED: ico entry {0} overruns file" -f $i) } + # frame-format gate: DIB (BITMAPINFOHEADER) below 256, PNG signature at 256 + $sig = ('{0:X2} {1:X2} {2:X2} {3:X2}' -f $check[$off], $check[$off + 1], $check[$off + 2], $check[$off + 3]) + $want = $(if ($sorted[$i] -ge 256) { '89 50 4E 47' } else { '28 00 00 00' }) + if ($sig -ne $want) { throw ("FAILED: ico entry {0} (size {1}) frame sig {2}, expected {3} - shell would render it blank" -f $i, $sorted[$i], $sig, $want) } $total += $len } Write-Host (" [ok] {0} ({1} KB, {2} images: {3}, {4} KB image data)" -f ` diff --git a/tools/opencode-blue-branding/tray_icon.ico b/tools/opencode-blue-branding/tray_icon.ico index 3dc3f66..18a96fa 100644 Binary files a/tools/opencode-blue-branding/tray_icon.ico and b/tools/opencode-blue-branding/tray_icon.ico differ diff --git a/tools/opencode-green-branding/tray_icon.ico b/tools/opencode-green-branding/tray_icon.ico index 8619596..a1afca2 100644 Binary files a/tools/opencode-green-branding/tray_icon.ico and b/tools/opencode-green-branding/tray_icon.ico differ diff --git a/tools/opencode-orange-branding/tray_icon.ico b/tools/opencode-orange-branding/tray_icon.ico index a856dbd..e4b369a 100644 Binary files a/tools/opencode-orange-branding/tray_icon.ico and b/tools/opencode-orange-branding/tray_icon.ico differ diff --git a/tools/opencode-red-branding/tray_icon.ico b/tools/opencode-red-branding/tray_icon.ico index e3833f8..9c8f303 100644 Binary files a/tools/opencode-red-branding/tray_icon.ico and b/tools/opencode-red-branding/tray_icon.ico differ diff --git a/tools/opencode-yellow-branding/tray_icon.ico b/tools/opencode-yellow-branding/tray_icon.ico index e5b9732..f95e7b9 100644 Binary files a/tools/opencode-yellow-branding/tray_icon.ico and b/tools/opencode-yellow-branding/tray_icon.ico differ