claude-proxy: native C# GUI to force Claude desktop traffic through a proxy

Windows tray "switch" that routes all Claude MSIX-app traffic through a
SOCKS5/HTTP proxy or a VLESS+Reality tunnel (embedded Xray-core), with a
fail-closed Windows Firewall lock. Runs de-elevated so it launches the
user's normal Claude instance; elevates only for firewall changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 13:46:23 +03:00
commit ab2d3ed8cd
16 changed files with 2707 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
<#
make-icon.ps1 - generate a Claude-styled multi-size app icon (icon.ico) + preview.png.
Coral rounded tile with a white radial "spark" mark.
#>
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Drawing
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
function New-Tile([int]$S) {
$bmp = New-Object System.Drawing.Bitmap($S, $S, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
$g = [System.Drawing.Graphics]::FromImage($bmp)
$g.SmoothingMode = 'AntiAlias'
$g.InterpolationMode = 'HighQualityBicubic'
$g.PixelOffsetMode = 'HighQuality'
$g.Clear([System.Drawing.Color]::Transparent)
# rounded tile
$pad = [math]::Max(1, [int]($S * 0.06))
$rect = New-Object System.Drawing.Rectangle($pad, $pad, ($S - 2 * $pad), ($S - 2 * $pad))
$rad = [int]($S * 0.225)
$path = New-Object System.Drawing.Drawing2D.GraphicsPath
$d = $rad * 2
$path.AddArc($rect.X, $rect.Y, $d, $d, 180, 90)
$path.AddArc($rect.Right - $d, $rect.Y, $d, $d, 270, 90)
$path.AddArc($rect.Right - $d, $rect.Bottom - $d, $d, $d, 0, 90)
$path.AddArc($rect.X, $rect.Bottom - $d, $d, $d, 90, 90)
$path.CloseFigure()
# coral gradient
$c1 = [System.Drawing.Color]::FromArgb(226, 138, 101) # lighter coral
$c2 = [System.Drawing.Color]::FromArgb(193, 95, 60) # deeper terracotta
$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush($rect, $c1, $c2, 90)
$g.FillPath($brush, $path)
# subtle top sheen
$sheen = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
$rect, [System.Drawing.Color]::FromArgb(38, 255, 255, 255), [System.Drawing.Color]::FromArgb(0, 255, 255, 255), 90)
$g.FillPath($sheen, $path)
# toggle-switch glyph (white track + coral knob) - the "рубильник", distinct from Claude's spark
$tw = $S * 0.54; $th = $S * 0.30
$tx = ($S - $tw) / 2.0; $ty = ($S - $th) / 2.0
$tp = New-Object System.Drawing.Drawing2D.GraphicsPath
$tp.AddArc($tx, $ty, $th, $th, 90, 180)
$tp.AddArc($tx + $tw - $th, $ty, $th, $th, 270, 180)
$tp.CloseFigure()
$trackBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(240, 248, 246, 239))
$g.FillPath($trackBrush, $tp)
# knob on the ON (right) side, coral so it reads as cut out of the white track
$knobD = $th - ($S * 0.09)
$knobX = $tx + $tw - $knobD - ($S * 0.045)
$knobY = $ty + (($th - $knobD) / 2.0)
$knobBrush = New-Object System.Drawing.SolidBrush($c2)
$g.FillEllipse($knobBrush, $knobX, $knobY, $knobD, $knobD)
$g.Dispose(); $brush.Dispose(); $sheen.Dispose(); $trackBrush.Dispose(); $knobBrush.Dispose(); $tp.Dispose()
$bmp
}
# preview
$preview = New-Tile 256
$preview.Save((Join-Path $root 'preview.png'), [System.Drawing.Imaging.ImageFormat]::Png)
# build .ico (PNG-compressed entries, Vista+)
$sizes = @(16, 24, 32, 48, 64, 128, 256)
$pngs = @()
foreach ($s in $sizes) {
$b = New-Tile $s
$ms = New-Object System.IO.MemoryStream
$b.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
$pngs += , @{ size = $s; bytes = $ms.ToArray() }
$b.Dispose(); $ms.Dispose()
}
$icoPath = Join-Path $root 'icon.ico'
$fs = New-Object System.IO.FileStream($icoPath, [System.IO.FileMode]::Create)
$bw = New-Object System.IO.BinaryWriter($fs)
# ICONDIR
$bw.Write([uint16]0) # reserved
$bw.Write([uint16]1) # type = icon
$bw.Write([uint16]$pngs.Count) # count
$offset = 6 + 16 * $pngs.Count
foreach ($p in $pngs) {
$wh = if ($p.size -ge 256) { 0 } else { $p.size }
$bw.Write([byte]$wh) # width
$bw.Write([byte]$wh) # height
$bw.Write([byte]0) # palette
$bw.Write([byte]0) # reserved
$bw.Write([uint16]1) # planes
$bw.Write([uint16]32) # bpp
$bw.Write([uint32]$p.bytes.Length)
$bw.Write([uint32]$offset)
$offset += $p.bytes.Length
}
foreach ($p in $pngs) { $bw.Write($p.bytes) }
$bw.Flush(); $bw.Close(); $fs.Close()
Write-Host "icon.ico : $icoPath ($([math]::Round((Get-Item $icoPath).Length/1KB)) KB, sizes: $($sizes -join ','))"
Write-Host "preview : $(Join-Path $root 'preview.png')"