# Icônes launcher depuis le logo — Plan d'implémentation > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Décliner `docs/branding/logo.svg` en icônes launcher Android (adaptive + legacy), iOS (appiconset) et desktop, via `docs/branding/generate-icons.sh`. **Architecture:** 3 variantes SVG committées (square / foreground / monochrome) → script bash idempotent avec `rsvg-convert` (+ `python3` pour les tailles iOS lues dans `Contents.json`, + `magick` pour le background uni) qui écrase les PNG existants in situ. **Tech Stack:** SVG 1.1, bash, rsvg-convert 2.62, python3, ImageMagick, Compose Multiplatform (`painterResource`). ## Global Constraints - Palette uniquement : `#1D3557` `#457B9D` `#A8DADC` `#E63946` `#F1FAEE` (+ `#FFFFFF` réservé à la couche monochrome) - Aucun `` dans les SVG ; aucune référence externe - Zone sûre Android : cercle de 66 dp de diamètre centré dans le canvas 108 (scale 0.5073) - `Contents.json` iOS non modifié ; chemins/noms des PNG Android inchangés - Géométrie tuiles/glyphes : verbatim du master `logo.svg` (spec 2026-08-31-logo-design §3) --- ### Task 1: Variantes SVG sources **Files:** - Create: `docs/branding/icon-square.svg` - Create: `docs/branding/icon-foreground.svg` - Create: `docs/branding/icon-monochrome.svg` **Interfaces:** - Consumes: géométrie du master `logo.svg` - Produces: 3 SVG sources consommés par `generate-icons.sh` (Task 2) - [ ] **Step 1: Créer `icon-square.svg`** (plein cadre, sans arrondi — pour iOS) ```svg Livrets — icône plein cadre ``` - [ ] **Step 2: Créer `icon-foreground.svg`** (viewBox 108, scale 0.5073 vers centre 54,54) ```svg Livrets — couche foreground adaptive ``` - [ ] **Step 3: Créer `icon-monochrome.svg`** (tuiles blanches uniquement, même groupe) ```svg Livrets — couche monochrome adaptive ``` - [ ] **Step 4: Valider** ```bash for f in docs/branding/icon-square.svg docs/branding/icon-foreground.svg docs/branding/icon-monochrome.svg; do xmllint --noout "$f" && grep -c "/dev/null 2>&1 || { echo "Erreur : '$tool' requis" >&2; exit 1; } done # --- Android : legacy (carte arrondie du master) --- for pair in mdpi:48 hdpi:72 xhdpi:96 xxhdpi:144 xxxhdpi:192; do dpi="${pair%%:*}"; size="${pair##*:}" rsvg-convert -w "$size" -h "$size" "$BRANDING/logo.svg" -o "$RES/mipmap-$dpi/ic_launcher.png" done # --- Android : couches adaptive (108 dp) --- for pair in mdpi:108 hdpi:162 xhdpi:216 xxhdpi:324 xxxhdpi:432; do dpi="${pair%%:*}"; size="${pair##*:}" magick -size "${size}x${size}" xc:'#1D3557' "$RES/mipmap-$dpi/ic_launcher_background.png" rsvg-convert -w "$size" -h "$size" "$BRANDING/icon-foreground.svg" -o "$RES/mipmap-$dpi/ic_launcher_foreground.png" rsvg-convert -w "$size" -h "$size" "$BRANDING/icon-monochrome.svg" -o "$RES/mipmap-$dpi/ic_launcher_monochrome.png" done # --- iOS : tailles lues dans Contents.json --- python3 - "$BRANDING/icon-square.svg" "$IOS_APPSET" <<'PY' import json, pathlib, subprocess, sys svg, appset = sys.argv[1], pathlib.Path(sys.argv[2]) for img in json.loads((appset / "Contents.json").read_text())["images"]: w, _ = (float(v) for v in img["size"].split("x")) px = round(w * float(img["scale"].rstrip("x"))) subprocess.run( ["rsvg-convert", "-w", str(px), "-h", str(px), svg, "-o", str(appset / img["filename"])], check=True, ) PY # --- Desktop --- mkdir -p "$ROOT/desktopApp/src/main/resources/icons" rsvg-convert -w 512 -h 512 "$BRANDING/logo.svg" -o "$ROOT/desktopApp/src/main/resources/icons/icon.png" echo "Icônes générées ✓" ``` - [ ] **Step 2: Rendre exécutable et lancer** ```bash chmod +x docs/branding/generate-icons.sh ./docs/branding/generate-icons.sh # attendu : « Icônes générées ✓ » sans erreur ``` - [ ] **Step 3: Vérifier les dimensions générées** ```bash sips -g pixelWidth androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png | grep -q 192 # legacy sips -g pixelWidth androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png | grep -q 432 sips -g pixelWidth iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png | grep -q 1024 sips -g pixelWidth desktopApp/src/main/resources/icons/icon.png | grep -q 512 # attendu : les 4 grep réussissent (silence = OK) for f in iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/*.png; do sips -g pixelWidth "$f" | grep -qE "[1-9][0-9]*"; done # attendu : tous les PNG iOS existent et non vides ``` - [ ] **Step 4: Contrôle visuel companion** (grille des icônes + simulation masque rond Android) puis commit ```bash git add androidApp/src/main/res docs/branding/generate-icons.sh desktopApp/src/main/resources iosApp/iosApp/Assets.xcassets git commit -m "feat: générer les icônes launcher depuis le logo (script + assets)" ``` --- ### Task 3: Icône de fenêtre desktop **Files:** - Modify: `desktopApp/src/main/kotlin/com/livrets/desktop/Main.kt:3-6` (imports) et `:10-14` (Window) **Interfaces:** - Consumes: `desktopApp/src/main/resources/icons/icon.png` (Task 2) - Produces: fenêtre desktop icônée - [ ] **Step 1: Ajouter l'import et le paramètre icon** ```kotlin import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import com.livrets.App import org.jetbrains.compose.ui.res.painterResource fun main() = application { Window( onCloseRequest = ::exitApplication, title = "Livrets", state = rememberWindowState(width = 480.dp, height = 800.dp), icon = painterResource("icons/icon.png"), ) { App(filesDirPath = storageDir()) } } private fun storageDir(): String = System.getProperty("user.home") + "/.livrets" ``` - [ ] **Step 2: Compiler et tester** ```bash ./gradlew :desktopApp:compileKotlin --console=plain 2>&1 | tail -3 # attendu : BUILD SUCCESSFUL (sinon ajuster l'import painterResource selon la version compose) ./gradlew :sharedUI:jvmTest :androidApp:assembleDebug --console=plain 2>&1 | tail -3 # attendu : BUILD SUCCESSFUL ``` - [ ] **Step 3: Commit** ```bash git add desktopApp/src/main/kotlin/com/livrets/desktop/Main.kt git commit -m "feat: icône de fenêtre desktop" ```