210 lines
7.7 KiB
PowerShell
210 lines
7.7 KiB
PowerShell
|
|
# ============================================================
|
||
|
|
# 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"
|