11/**
22 * Generate app icons from the SVG logo for all platforms
33 *
4- * - macOS: icon.icns (multiple sizes bundled)
4+ * - macOS: icon.icns (multiple sizes bundled, with ~10% padding per Apple HIG )
55 * - Windows: icon.ico (multiple sizes bundled)
66 * - Linux: icon.png (256x256)
77 */
@@ -18,6 +18,50 @@ const LOGO_PATH = join(ROOT_DIR, "src", "browser", "logo.svg");
1818// Icon sizes needed for each platform
1919const ICON_SIZES = [ 16 , 32 , 48 , 64 , 128 , 256 , 512 , 1024 ] ;
2020
21+ // macOS icons need padding around the icon to match Apple HIG
22+ // Standard macOS icons have ~10% padding on each side (icon fills ~80% of canvas)
23+ const MACOS_ICON_SCALE = 0.8 ;
24+
25+ /**
26+ * Generate a PNG with padding for macOS icons
27+ * macOS desktop icons (Finder, Dock, etc.) have standard padding/safe zone
28+ */
29+ async function generateMacOSIcon (
30+ svgBuffer : Buffer ,
31+ size : number
32+ ) : Promise < Buffer > {
33+ const iconSize = Math . round ( size * MACOS_ICON_SCALE ) ;
34+ const padding = Math . round ( ( size - iconSize ) / 2 ) ;
35+
36+ // First resize the SVG to the smaller icon size
37+ const resizedIcon = await sharp ( svgBuffer )
38+ . resize ( iconSize , iconSize , {
39+ fit : "contain" ,
40+ background : { r : 0 , g : 0 , b : 0 , alpha : 0 } ,
41+ } )
42+ . png ( )
43+ . toBuffer ( ) ;
44+
45+ // Then composite onto a transparent canvas with padding
46+ return sharp ( {
47+ create : {
48+ width : size ,
49+ height : size ,
50+ channels : 4 ,
51+ background : { r : 0 , g : 0 , b : 0 , alpha : 0 } ,
52+ } ,
53+ } )
54+ . composite ( [
55+ {
56+ input : resizedIcon ,
57+ left : padding ,
58+ top : padding ,
59+ } ,
60+ ] )
61+ . png ( )
62+ . toBuffer ( ) ;
63+ }
64+
2165async function generateIcons ( ) {
2266 console . log ( "📦 Generating app icons from SVG...\n" ) ;
2367
@@ -29,7 +73,7 @@ async function generateIcons() {
2973 // Read and convert SVG to high-res PNG first
3074 const svgBuffer = readFileSync ( LOGO_PATH ) ;
3175
32- // Generate PNGs at various sizes
76+ // Generate PNGs at various sizes (for Windows/Linux - no padding)
3377 const pngBuffers : Map < number , Buffer > = new Map ( ) ;
3478
3579 for ( const size of ICON_SIZES ) {
@@ -44,6 +88,14 @@ async function generateIcons() {
4488 pngBuffers . set ( size , buffer ) ;
4589 }
4690
91+ // Generate macOS-specific icons with padding
92+ const macPngBuffers : Map < number , Buffer > = new Map ( ) ;
93+ for ( const size of ICON_SIZES ) {
94+ console . log ( ` Generating ${ size } x${ size } macOS PNG (with padding)...` ) ;
95+ const buffer = await generateMacOSIcon ( svgBuffer , size ) ;
96+ macPngBuffers . set ( size , buffer ) ;
97+ }
98+
4799 // Save the main 256x256 PNG for Linux and general use
48100 const png256 = pngBuffers . get ( 256 ) ! ;
49101 writeFileSync ( join ( BUILD_DIR , "icon.png" ) , png256 ) ;
@@ -53,11 +105,11 @@ async function generateIcons() {
53105 const png512 = pngBuffers . get ( 512 ) ! ;
54106 writeFileSync ( join ( BUILD_DIR , "icon@2x.png" ) , png512 ) ;
55107
56- // Generate macOS .icns
57- console . log ( "\n Generating macOS icon.icns..." ) ;
108+ // Generate macOS .icns (using padded icons for proper macOS appearance)
109+ console . log ( "\n Generating macOS icon.icns (with padding) ..." ) ;
58110 try {
59- // Use 1024x1024 as the source for best quality
60- const png1024 = pngBuffers . get ( 1024 ) ! ;
111+ // Use 1024x1024 padded version as the source for best quality
112+ const png1024 = macPngBuffers . get ( 1024 ) ! ;
61113 const icns = png2icons . createICNS ( png1024 , png2icons . BICUBIC2 , 0 ) ;
62114 if ( icns ) {
63115 writeFileSync ( join ( BUILD_DIR , "icon.icns" ) , icns ) ;
0 commit comments