-
Notifications
You must be signed in to change notification settings - Fork 8.3k
fix: Resize macOS app icon to 1024×1024 with proper safe area #1771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # Icon Conversion Guide | ||
|
|
||
| The SVG has been updated to 1024×1024 with proper 10% safe area margins. Now you need to convert it to PNG. | ||
|
|
||
| ## Current Status | ||
|
|
||
| ✅ **SVG Updated**: `apps/web/public/app-icon.svg` is now 1024×1024 with 10% safe area | ||
| ❌ **PNG Needs Update**: `tools/pack/resources/mac/icon.png` is still 533×533 | ||
|
|
||
| ## Conversion Options | ||
|
|
||
| ### Option 1: Using Node.js with sharp (Recommended) | ||
|
|
||
| ```bash | ||
| pnpm add -D sharp | ||
| node scripts/convert-icon.mjs | ||
| ``` | ||
|
|
||
| ### Option 2: Using Browser (Zero Dependencies) | ||
|
|
||
| 1. Open `scripts/convert-icon.html` in a browser | ||
| 2. Click "Convert SVG to PNG" | ||
| 3. Save the downloaded file to `tools/pack/resources/mac/icon.png` | ||
|
|
||
| ### Option 3: Using ImageMagick (if available) | ||
|
|
||
| ```bash | ||
| convert -background none -resize 1024x1024 apps/web/public/app-icon.svg tools/pack/resources/mac/icon.png | ||
| ``` | ||
|
|
||
| ### Option 4: Using Inkscape (if available) | ||
|
|
||
| ```bash | ||
| inkscape apps/web/public/app-icon.svg --export-type=png --export-filename=tools/pack/resources/mac/icon.png -w 1024 -h 1024 | ||
| ``` | ||
|
|
||
| ### Option 5: Using Python with cairosvg | ||
|
|
||
| ```bash | ||
| pip install cairosvg | ||
| python3 -c "import cairosvg; cairosvg.svg2png(url='apps/web/public/app-icon.svg', write_to='tools/pack/resources/mac/icon.png', output_width=1024, output_height=1024)" | ||
| ``` | ||
|
|
||
| ### Option 6: Using rsvg-convert | ||
|
|
||
| ```bash | ||
| rsvg-convert -w 1024 -h 1024 apps/web/public/app-icon.svg -o tools/pack/resources/mac/icon.png | ||
| ``` | ||
|
|
||
| ## Verification | ||
|
|
||
| After conversion, verify the PNG is correct: | ||
|
|
||
| ```bash | ||
| file tools/pack/resources/mac/icon.png | ||
| # Should show: PNG image data, 1024 x 1024 | ||
| ``` | ||
|
|
||
| ## What Changed | ||
|
|
||
| - **Canvas size**: 533×533 → 1024×1024 | ||
| - **Safe area margin**: ~13px (2.4%) → ~102px (10%) | ||
| - **Content scaling**: 1.5385× to fit within safe area | ||
| - **Visual result**: Icon now has proper padding for macOS Launchpad and Dock | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| #!/usr/bin/env node | ||
| /** | ||
| * Convert app-icon.svg to icon.png at 1024x1024 using canvas | ||
| * This version uses @napi-rs/canvas which might already be available | ||
| */ | ||
|
|
||
| import { createCanvas, loadImage } from '@napi-rs/canvas'; | ||
| import { readFileSync, writeFileSync } from 'fs'; | ||
| import { fileURLToPath } from 'url'; | ||
| import { dirname, join } from 'path'; | ||
|
|
||
| const __filename = fileURLToPath(import.meta.url); | ||
| const __dirname = dirname(__filename); | ||
| const projectRoot = join(__dirname, '..'); | ||
|
|
||
| const svgPath = join(projectRoot, 'apps/web/public/app-icon.svg'); | ||
| const pngPath = join(projectRoot, 'tools/pack/resources/mac/icon.png'); | ||
|
|
||
| console.log('Converting SVG to PNG using canvas...'); | ||
| console.log(`Input: ${svgPath}`); | ||
| console.log(`Output: ${pngPath}`); | ||
|
|
||
| try { | ||
| const svgBuffer = readFileSync(svgPath); | ||
| const img = await loadImage(svgBuffer); | ||
|
|
||
| const canvas = createCanvas(1024, 1024); | ||
| const ctx = canvas.getContext('2d'); | ||
|
|
||
| ctx.drawImage(img, 0, 0, 1024, 1024); | ||
|
|
||
| const pngBuffer = canvas.toBuffer('image/png'); | ||
| writeFileSync(pngPath, pngBuffer); | ||
|
|
||
| console.log('✓ Conversion complete!'); | ||
| console.log('Icon is now 1024×1024 with 10% safe area margins'); | ||
| } catch (error) { | ||
| console.error('✗ Conversion failed:', error.message); | ||
| console.error('Try installing sharp: pnpm add -D sharp'); | ||
| console.error('Then use: node scripts/convert-icon.mjs'); | ||
| process.exit(1); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>SVG to PNG Converter</title> | ||
| <style> | ||
| body { | ||
| font-family: system-ui, -apple-system, sans-serif; | ||
| max-width: 800px; | ||
| margin: 50px auto; | ||
| padding: 20px; | ||
| } | ||
| #canvas { | ||
| border: 1px solid #ccc; | ||
| margin: 20px 0; | ||
| } | ||
| button { | ||
| padding: 10px 20px; | ||
| font-size: 16px; | ||
| cursor: pointer; | ||
| } | ||
| .info { | ||
| background: #f0f0f0; | ||
| padding: 15px; | ||
| border-radius: 5px; | ||
| margin: 20px 0; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <h1>SVG to PNG Converter</h1> | ||
| <div class="info"> | ||
| <p><strong>Instructions:</strong></p> | ||
| <ol> | ||
| <li>Open this file in a browser</li> | ||
| <li>Click "Convert SVG to PNG"</li> | ||
| <li>The PNG will download automatically</li> | ||
| <li>Save it as <code>tools/pack/resources/mac/icon.png</code></li> | ||
| </ol> | ||
| </div> | ||
|
|
||
| <button onclick="convertSvgToPng()">Convert SVG to PNG</button> | ||
|
|
||
| <div> | ||
| <canvas id="canvas" width="1024" height="1024"></canvas> | ||
| </div> | ||
|
|
||
| <div id="status"></div> | ||
|
|
||
| <script> | ||
| async function convertSvgToPng() { | ||
| const status = document.getElementById('status'); | ||
| status.textContent = 'Converting...'; | ||
|
|
||
| try { | ||
| // Load the SVG file | ||
| const response = await fetch('../apps/web/public/app-icon.svg'); | ||
| const svgText = await response.text(); | ||
|
|
||
| // Create an image from the SVG | ||
| const img = new Image(); | ||
| const svgBlob = new Blob([svgText], { type: 'image/svg+xml' }); | ||
| const url = URL.createObjectURL(svgBlob); | ||
|
|
||
| img.onload = function() { | ||
| // Draw to canvas | ||
| const canvas = document.getElementById('canvas'); | ||
| const ctx = canvas.getContext('2d'); | ||
| ctx.clearRect(0, 0, 1024, 1024); | ||
| ctx.drawImage(img, 0, 0, 1024, 1024); | ||
|
|
||
| // Convert to PNG and download | ||
| canvas.toBlob(function(blob) { | ||
| const link = document.createElement('a'); | ||
| link.download = 'icon.png'; | ||
| link.href = URL.createObjectURL(blob); | ||
| link.click(); | ||
|
|
||
| status.textContent = '✓ PNG downloaded! Save it to tools/pack/resources/mac/icon.png'; | ||
| status.style.color = 'green'; | ||
|
|
||
| URL.revokeObjectURL(url); | ||
| }, 'image/png'); | ||
| }; | ||
|
|
||
| img.onerror = function() { | ||
| status.textContent = '✗ Failed to load SVG'; | ||
| status.style.color = 'red'; | ||
| }; | ||
|
|
||
| img.src = url; | ||
|
|
||
| } catch (error) { | ||
| status.textContent = '✗ Error: ' + error.message; | ||
| status.style.color = 'red'; | ||
| } | ||
| } | ||
| </script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #!/usr/bin/env node | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These new project-owned |
||
| /** | ||
| * Convert app-icon.svg to icon.png at 1024x1024 | ||
| * | ||
| * Usage: | ||
| * node scripts/convert-icon.mjs | ||
| * | ||
| * Requirements: | ||
| * pnpm add -D sharp | ||
| */ | ||
|
|
||
| import sharp from 'sharp'; | ||
| import { fileURLToPath } from 'url'; | ||
| import { dirname, join } from 'path'; | ||
|
|
||
| const __filename = fileURLToPath(import.meta.url); | ||
| const __dirname = dirname(__filename); | ||
| const projectRoot = join(__dirname, '..'); | ||
|
|
||
| const svgPath = join(projectRoot, 'apps/web/public/app-icon.svg'); | ||
| const pngPath = join(projectRoot, 'tools/pack/resources/mac/icon.png'); | ||
|
|
||
| console.log('Converting SVG to PNG...'); | ||
| console.log(`Input: ${svgPath}`); | ||
| console.log(`Output: ${pngPath}`); | ||
|
|
||
| try { | ||
| await sharp(svgPath) | ||
| .resize(1024, 1024) | ||
| .png() | ||
| .toFile(pngPath); | ||
|
|
||
| console.log('✓ Conversion complete!'); | ||
| console.log('Icon is now 1024×1024 with 10% safe area margins'); | ||
| } catch (error) { | ||
| console.error('✗ Conversion failed:', error.message); | ||
| process.exit(1); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| #!/usr/bin/env python3 | ||
| from playwright.sync_api import sync_playwright | ||
|
|
||
| with sync_playwright() as p: | ||
| browser = p.chromium.launch() | ||
| page = browser.new_page() | ||
|
|
||
| # 读取 SVG | ||
| with open('apps/web/public/app-icon.svg', 'r') as f: | ||
| svg_content = f.read() | ||
|
|
||
| # 转义反引号 | ||
| svg_escaped = svg_content.replace('`', '\\`') | ||
|
|
||
| # 创建 HTML 页面来渲染 SVG | ||
| html = f''' | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <style> | ||
| body {{ margin: 0; padding: 0; }} | ||
| #canvas {{ display: block; }} | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <canvas id="canvas" width="1024" height="1024"></canvas> | ||
| <script> | ||
| const canvas = document.getElementById('canvas'); | ||
| const ctx = canvas.getContext('2d'); | ||
| const img = new Image(); | ||
| const svg = `{svg_escaped}`; | ||
| const blob = new Blob([svg], {{type: 'image/svg+xml'}}); | ||
| const url = URL.createObjectURL(blob); | ||
|
|
||
| img.onload = function() {{ | ||
| ctx.drawImage(img, 0, 0, 1024, 1024); | ||
| URL.revokeObjectURL(url); | ||
| }}; | ||
| img.src = url; | ||
| </script> | ||
| </body> | ||
| </html> | ||
| ''' | ||
|
|
||
| page.set_content(html) | ||
| page.wait_for_timeout(2000) # 等待渲染完成 | ||
|
|
||
| # 截图保存为 PNG | ||
| page.locator('#canvas').screenshot(path='tools/pack/resources/mac/icon.png') | ||
|
|
||
| browser.close() | ||
| print('✅ PNG 已生成: tools/pack/resources/mac/icon.png') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The instructions and helper scripts only regenerate
🔁 Powered by Looper · runner=reviewer · agent=opencode · model=openai/gpt-5.4 · An autonomous AI dev team for your GitHub repos.tools/pack/resources/mac/icon.png, but packaged mac builds still readtools/pack/resources/mac/icon.icns(tools/pack/src/resources.ts:35). As a result, the installed app will continue shipping the old icon and Launchpad/Dock will not reflect this fix even if the PNG is updated. Please regenerate and commiticon.icnsfrom the 1024×1024 source (or change the packaging config to consume the updated asset you are actually maintaining) as part of this PR.