AIMC
Retour

Code

Icône macOS dans Electron : le pipeline propre

Pourquoi une app Electron macOS doit passer par un vrai .icns dans le bundle, comment générer les assets, et pourquoi app.dock.setIcon ne doit pas être la source de vérité.

3 min lecture672 mots2026-05-02

#electron#macos

Sur macOS, l'icône d'une app Electron ne devrait pas être traitée comme une image qu'on force au runtime. Pour une app packagée, le résultat le plus propre vient d'un vrai fichier .icns déclaré dans le bundle .app.

Le réflexe classique consiste à ajouter un truc du genre dans electron/main.cts :

app.dock.setIcon(...)

Ça peut dépanner en dev. Mais pour une app distribuée, ce n'est pas la bonne source de vérité. macOS ne présente pas une app packagée comme une fenêtre Electron lancée à la main : il lit les métadonnées du bundle, son Info.plist, ses ressources, ses caches d'icônes, puis décide comment afficher l'app dans le Dock, Finder, Spotlight, Launchpad et les dialogues système.

Si tu veux un rendu natif, il faut donc jouer le jeu natif.

La règle

Pour HQ, la source de vérité est :

electron/assets/icon.icns

Et autour :

electron/assets/icon.png
electron/assets/icon-source/*

Le rôle de chaque fichier :

FichierRôle
electron/assets/icon.icnsIcône macOS finale pour l'app packagée
electron/assets/icon.pngIcône générique pour Electron, navigateur, previews
electron/assets/icon-source/*Sources éditables : fond, foreground, exports intermédiaires

Le .icns est le fichier que macOS attend pour une vraie app. Le .png est utile, mais il ne remplace pas le bundle icon.

Pourquoi pas app.dock.setIcon

app.dock.setIcon(...) agit au runtime. C'est donc une instruction donnée à l'app quand elle tourne déjà.

Problème : l'icône d'une app macOS packagée n'existe pas seulement "quand l'app tourne". Elle existe aussi dans le Finder, Launchpad, Spotlight, les permissions système, les caches d'icônes et les previews du bundle.

Avec un override runtime, tu peux obtenir un Dock correct en dev et un comportement différent une fois l'app packagée. Ou l'inverse. C'est précisément le genre de bug visuel qui fait perdre du temps parce qu'on teste le mauvais état.

La règle pratique :

  • Dev rapide : app.dock.setIcon(...) peut servir de preview temporaire.
  • App normale : ne pas le mettre dans le chemin principal.
  • Test final : toujours juger une .app packagée avec son .icns.

Pipeline actuel

Le pipeline se lance avec :

bun run icon:generate

Le générateur est dans :

scripts/generate-icon.ts

Il produit :

electron/assets/icon.svg
electron/assets/icon.png
electron/assets/icon.icns
electron/assets/icon-source/background.svg
electron/assets/icon-source/background.png
electron/assets/icon-source/foreground.svg
electron/assets/icon-source/foreground.png
electron/assets/icon-source/README.md

L'idée est simple : on garde des sources éditables, on exporte les formats intermédiaires, puis on fabrique un .icns moderne pour macOS.

Quand app-builder-bin est disponible, le script l'utilise pour générer l'icône finale. Ça permet d'obtenir un .icns récent, avec un type ic11, au lieu d'un fichier bricolé ou incomplet.

Vérifier l'icône

Après génération :

file electron/assets/icon.icns

Le résultat attendu ressemble à :

electron/assets/icon.icns: Mac OS X icon, ... "ic11" type

Ce check est volontairement bas niveau. Il évite de se contenter d'un "ça s'ouvre chez moi" alors que le fichier produit n'est pas dans la forme attendue par macOS moderne.

Dev Electron vs app packagée

bun run electron:dev lance Electron directement. Dans ce mode, macOS peut afficher une icône Electron, une icône cachée, ou un état qui dépend de ses caches locaux.

Ce n'est pas le test final.

Le vrai test :

  1. Générer l'icône.
  2. Packager l'app.
  3. Ouvrir la .app.
  4. Vérifier le Dock, Finder, Spotlight et Launchpad.

Si tu forces une icône en dev pour ton confort, documente-le comme override de développement. Ne le laisse pas devenir la logique normale de l'app.

Ce qu'il ne faut pas remettre

Dans le chemin principal de electron/main.cts, ne remets pas :

app.dock.setIcon(...)

La configuration de packaging doit pointer vers :

electron/assets/icon.icns

Et macOS doit lire l'icône depuis le bundle.

Mettre à jour l'icône

Le workflow propre :

  1. Modifier scripts/generate-icon.ts.
  2. Lancer bun run icon:generate.
  3. Vérifier que electron/assets/icon.icns est bien un fichier ic11.
  4. Packager l'app.
  5. Juger le résultat sur la .app, pas sur electron:dev.

Ça paraît plus strict qu'un simple setIcon, mais c'est justement ce qui évite les écarts entre dev, packaging et comportement natif macOS.