@@ -1834,15 +1834,42 @@ function log(msg) {
18341834 } catch ( _ ) { }
18351835}
18361836
1837- /** Windows: resolve before the first show() or the taskbar often keeps a blank/generic icon . */
1837+ /** Windows: resolve before the first show(); omit `icon` if empty so the shell can fall back to the exe . */
18381838async function resolveBrowserWindowIcon ( ) {
18391839 const fromFile = nativeImageFromAppIcon ( ) ;
18401840 if ( fromFile && ! fromFile . isEmpty ( ) ) return fromFile ;
18411841 if ( process . platform !== "win32" || ! app . isPackaged ) return fromFile ;
1842+
1843+ const thumbSize = { width : 256 , height : 256 } ;
1844+ const inAsarOnly = ( p ) => p . includes ( "app.asar" ) && ! p . includes ( "app.asar.unpacked" ) ;
1845+
1846+ // Shell-backed extraction: often works for the packaged .exe (embedded rcedit icon) when ICO buffer decode fails.
1847+ try {
1848+ if ( fs . existsSync ( process . execPath ) ) {
1849+ const img = await nativeImage . createThumbnailFromPath ( process . execPath , thumbSize ) ;
1850+ if ( img && ! img . isEmpty ( ) ) return img ;
1851+ }
1852+ } catch ( e ) {
1853+ try {
1854+ log ( `createThumbnailFromPath(exe): ${ e ?. message || e } ` ) ;
1855+ } catch ( _ ) { }
1856+ }
1857+
1858+ for ( const p of collectAppIconIcoCandidates ( ) ) {
1859+ if ( ! p || ! fs . existsSync ( p ) || inAsarOnly ( p ) ) continue ;
1860+ try {
1861+ const img = await nativeImage . createThumbnailFromPath ( p , thumbSize ) ;
1862+ if ( img && ! img . isEmpty ( ) ) return img ;
1863+ } catch ( e ) {
1864+ try {
1865+ log ( `createThumbnailFromPath(${ p } ): ${ e ?. message || e } ` ) ;
1866+ } catch ( _ ) { }
1867+ }
1868+ }
1869+
18421870 const shellPaths = [ process . execPath , ...collectAppIconIcoCandidates ( ) ] . filter ( ( p ) => {
18431871 if ( ! p || ! fs . existsSync ( p ) ) return false ;
1844- const inAsarArchive = p . includes ( "app.asar" ) && ! p . includes ( "app.asar.unpacked" ) ;
1845- return ! inAsarArchive ;
1872+ return ! inAsarOnly ( p ) ;
18461873 } ) ;
18471874 for ( const p of shellPaths ) {
18481875 for ( const size of [ "large" , "normal" , "small" ] ) {
@@ -1871,14 +1898,22 @@ async function createWindow() {
18711898 }
18721899
18731900 const windowIcon = await resolveBrowserWindowIcon ( ) ;
1874- if ( process . platform === "win32" && app . isPackaged && ( ! windowIcon || windowIcon . isEmpty ( ) ) ) {
1901+ const iconForWindow = windowIcon && ! windowIcon . isEmpty ( ) ? windowIcon : undefined ;
1902+ if ( process . platform === "win32" && app . isPackaged && ! iconForWindow ) {
18751903 try {
18761904 log (
1877- `warn: taskbar icon empty; candidates =${ collectAppIconIcoCandidates ( ) . join ( " | " ) } exe=${ process . execPath } ` ,
1905+ `warn: window icon unresolved; resourcesPath= ${ process . resourcesPath } ico =${ collectAppIconIcoCandidates ( ) . join ( " | " ) } exe=${ process . execPath } ` ,
18781906 ) ;
18791907 } catch ( _ ) { }
18801908 }
18811909
1910+ const applyWindowIcon = ( ) => {
1911+ if ( ! iconForWindow || mainWindow . isDestroyed ( ) ) return ;
1912+ try {
1913+ mainWindow . setIcon ( iconForWindow ) ;
1914+ } catch ( _ ) { }
1915+ } ;
1916+
18821917 // NSIS close-app uses PRODUCT_NAME (package.json → build.productName). The window title must
18831918 // match that string, not a URL — otherwise the installer cannot find/close the running app.
18841919 // Keep in sync with app/package.json "build.productName".
@@ -1888,7 +1923,7 @@ async function createWindow() {
18881923 width : 1200 ,
18891924 height : 800 ,
18901925 title : windowTitle ,
1891- icon : windowIcon ,
1926+ ... ( iconForWindow ? { icon : iconForWindow } : { } ) ,
18921927 // Match app dark background (theme.ts); reduces flash and helps menu/client seam blend on Windows.
18931928 backgroundColor : "#111111" ,
18941929 webPreferences : {
@@ -1904,12 +1939,15 @@ async function createWindow() {
19041939 } catch ( _ ) { }
19051940
19061941 mainWindow . once ( "ready-to-show" , ( ) => {
1942+ applyWindowIcon ( ) ;
19071943 try {
19081944 mainWindow . maximize ( ) ;
19091945 } catch ( _ ) { }
19101946 mainWindow . show ( ) ;
19111947 } ) ;
19121948
1949+ mainWindow . webContents . once ( "did-finish-load" , applyWindowIcon ) ;
1950+
19131951 mainWindow . webContents . on ( "page-title-updated" , ( e ) => {
19141952 e . preventDefault ( ) ;
19151953 mainWindow . setTitle ( windowTitle ) ;
0 commit comments