# ============================================================ # Atomizer Desktop Shortcut Creator # Creates a desktop shortcut with custom "A" icon # ============================================================ $ErrorActionPreference = "Stop" # Paths $AtomizerRoot = Split-Path -Parent $PSScriptRoot $ToolsDir = $PSScriptRoot $IconPath = Join-Path $ToolsDir "atomizer.ico" $LauncherPath = Join-Path $ToolsDir "launch_atomizer.bat" $DesktopPath = [Environment]::GetFolderPath("Desktop") $ShortcutPath = Join-Path $DesktopPath "Atomizer.lnk" Write-Host "" Write-Host " Atomizer Desktop Shortcut Creator" -ForegroundColor Cyan Write-Host " ===================================" -ForegroundColor Cyan Write-Host "" # Step 1: Create ICO file with "A" letter Write-Host "[1/3] Creating icon file..." -ForegroundColor Yellow # Generate a simple ICO file using .NET Add-Type -AssemblyName System.Drawing function Create-AtomizerIcon { param([string]$OutputPath) # Create multiple icon sizes for best quality $sizes = @(256, 48, 32, 16) $bitmaps = @() foreach ($size in $sizes) { $bitmap = New-Object System.Drawing.Bitmap($size, $size) $graphics = [System.Drawing.Graphics]::FromImage($bitmap) # High quality rendering $graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $graphics.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::AntiAliasGridFit # Background - gradient blue (Atomizer brand color) $brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush( (New-Object System.Drawing.Point(0, 0)), (New-Object System.Drawing.Point($size, $size)), [System.Drawing.Color]::FromArgb(255, 59, 130, 246), # Blue-500 [System.Drawing.Color]::FromArgb(255, 37, 99, 235) # Blue-600 ) # Rounded rectangle background $path = New-Object System.Drawing.Drawing2D.GraphicsPath $radius = [int]($size * 0.15) $rect = New-Object System.Drawing.Rectangle(0, 0, $size, $size) $path.AddArc($rect.X, $rect.Y, $radius * 2, $radius * 2, 180, 90) $path.AddArc($rect.Right - $radius * 2, $rect.Y, $radius * 2, $radius * 2, 270, 90) $path.AddArc($rect.Right - $radius * 2, $rect.Bottom - $radius * 2, $radius * 2, $radius * 2, 0, 90) $path.AddArc($rect.X, $rect.Bottom - $radius * 2, $radius * 2, $radius * 2, 90, 90) $path.CloseFigure() $graphics.FillPath($brush, $path) # Draw "A" letter $fontSize = [int]($size * 0.65) $font = New-Object System.Drawing.Font("Segoe UI", $fontSize, [System.Drawing.FontStyle]::Bold) $textBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White) $format = New-Object System.Drawing.StringFormat $format.Alignment = [System.Drawing.StringAlignment]::Center $format.LineAlignment = [System.Drawing.StringAlignment]::Center $textRect = New-Object System.Drawing.RectangleF(0, 0, $size, $size) $graphics.DrawString("A", $font, $textBrush, $textRect, $format) # Cleanup $graphics.Dispose() $font.Dispose() $textBrush.Dispose() $brush.Dispose() $path.Dispose() $bitmaps += $bitmap } # Save as ICO (using the largest bitmap, Windows will auto-select appropriate size) # For proper multi-size ICO, we'll save the 256x256 and let Windows handle it $icon = [System.Drawing.Icon]::FromHandle($bitmaps[0].GetHicon()) # Write ICO file manually with all sizes $fs = [System.IO.File]::Create($OutputPath) $bw = New-Object System.IO.BinaryWriter($fs) # ICO Header $bw.Write([UInt16]0) # Reserved $bw.Write([UInt16]1) # Type (1 = ICO) $bw.Write([UInt16]$sizes.Count) # Number of images # Calculate offsets $headerSize = 6 + ($sizes.Count * 16) # Main header + directory entries $currentOffset = $headerSize $imageData = @() # Prepare image data and write directory entries for ($i = 0; $i -lt $sizes.Count; $i++) { $bmp = $bitmaps[$i] $ms = New-Object System.IO.MemoryStream $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) $pngBytes = $ms.ToArray() $ms.Dispose() $imageData += ,$pngBytes # Directory entry $width = if ($sizes[$i] -ge 256) { 0 } else { $sizes[$i] } $height = if ($sizes[$i] -ge 256) { 0 } else { $sizes[$i] } $bw.Write([byte]$width) # Width $bw.Write([byte]$height) # Height $bw.Write([byte]0) # Color palette $bw.Write([byte]0) # Reserved $bw.Write([UInt16]1) # Color planes $bw.Write([UInt16]32) # Bits per pixel $bw.Write([UInt32]$pngBytes.Length) # Image size $bw.Write([UInt32]$currentOffset) # Offset $currentOffset += $pngBytes.Length } # Write image data foreach ($data in $imageData) { $bw.Write($data) } $bw.Close() $fs.Close() # Cleanup bitmaps foreach ($bmp in $bitmaps) { $bmp.Dispose() } return $true } try { Create-AtomizerIcon -OutputPath $IconPath Write-Host " Created: $IconPath" -ForegroundColor Green } catch { Write-Host " Warning: Could not create custom icon. Using default." -ForegroundColor Yellow Write-Host " Error: $_" -ForegroundColor DarkGray $IconPath = $null } # Step 2: Create desktop shortcut Write-Host "[2/3] Creating desktop shortcut..." -ForegroundColor Yellow $WshShell = New-Object -ComObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($ShortcutPath) $Shortcut.TargetPath = $LauncherPath $Shortcut.WorkingDirectory = $AtomizerRoot $Shortcut.Description = "Launch Atomizer Optimization Dashboard" $Shortcut.WindowStyle = 1 # Normal window if ($IconPath -and (Test-Path $IconPath)) { $Shortcut.IconLocation = "$IconPath,0" } $Shortcut.Save() Write-Host " Created: $ShortcutPath" -ForegroundColor Green # Step 3: Verify Write-Host "[3/3] Verifying installation..." -ForegroundColor Yellow $success = $true if (-not (Test-Path $ShortcutPath)) { Write-Host " ERROR: Shortcut not created!" -ForegroundColor Red $success = $false } if (-not (Test-Path $LauncherPath)) { Write-Host " ERROR: Launcher script missing!" -ForegroundColor Red $success = $false } if ($success) { Write-Host "" Write-Host "============================================================" -ForegroundColor Green Write-Host " SUCCESS! Atomizer shortcut created on your desktop." -ForegroundColor Green Write-Host "============================================================" -ForegroundColor Green Write-Host "" Write-Host " Double-click 'Atomizer' on your desktop to launch!" -ForegroundColor White Write-Host "" Write-Host " What happens when you click:" -ForegroundColor Cyan Write-Host " 1. Backend server starts (FastAPI on port 8000)" -ForegroundColor Gray Write-Host " 2. Frontend server starts (Vite on port 5173)" -ForegroundColor Gray Write-Host " 3. Browser opens to http://localhost:5173" -ForegroundColor Gray Write-Host "" Write-Host " To stop: Close the 'Atomizer Backend' and 'Atomizer Frontend'" -ForegroundColor Cyan Write-Host " terminal windows." -ForegroundColor Cyan Write-Host "" } else { Write-Host "" Write-Host "Installation had errors. Please check the messages above." -ForegroundColor Red Write-Host "" } # Keep window open Read-Host "Press Enter to close"