From fd929e6c8ebf1e132294dcabbd57036f3195e70c Mon Sep 17 00:00:00 2001 From: Rene Luria Date: Mon, 31 Aug 2026 09:18:03 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20plan=20ic=C3=B4nes=20launcher=20et=20co?= =?UTF-8?q?rrection=20zone=20s=C3=BBre=20circulaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-08-31-icons-launcher.md | 245 ++++++++++++++++++ .../specs/2026-08-31-icons-launcher-design.md | 4 +- 2 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 docs/superpowers/plans/2026-08-31-icons-launcher.md diff --git a/docs/superpowers/plans/2026-08-31-icons-launcher.md b/docs/superpowers/plans/2026-08-31-icons-launcher.md new file mode 100644 index 0000000..6e3c161 --- /dev/null +++ b/docs/superpowers/plans/2026-08-31-icons-launcher.md @@ -0,0 +1,245 @@ +# 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" +``` diff --git a/docs/superpowers/specs/2026-08-31-icons-launcher-design.md b/docs/superpowers/specs/2026-08-31-icons-launcher-design.md index ec80142..81bf98c 100644 --- a/docs/superpowers/specs/2026-08-31-icons-launcher-design.md +++ b/docs/superpowers/specs/2026-08-31-icons-launcher-design.md @@ -14,7 +14,7 @@ Décliner le logo maître (`docs/branding/logo.svg`, spec 2026-08-31-logo-design | Pipeline | Script `docs/branding/generate-icons.sh` + `rsvg-convert` (déjà installé) ; `python3` pour lire `Contents.json` iOS | | Rejetés | Tâche Gradle custom (lourde pour un usage occasionnel) ; PNGs sans script (non reproductible) | | Variantes SVG | 4 fichiers dans `docs/branding/` : `logo.svg` (master), `icon-square.svg`, `icon-foreground.svg`, `icon-monochrome.svg` | -| Android adaptive | background = bleu nuit uni plein cadre ; foreground et monochrome réduits (~72 %) pour tenir dans la zone sûre 66/108 | +| Android adaptive | background = bleu nuit uni plein cadre ; foreground et monochrome réduits (~51 %) pour tenir dans le cercle de sûreté de 66 dp de **diamètre** (les 4 coins de la grille doivent y rester) | | Android legacy | Rendu du master (carte arrondie), 48→192 px | | iOS | PNG remplacés in situ, tailles lues dans `Contents.json` existant (ce fichier n'est pas modifié) | | Desktop | PNG 512 px dans les ressources + `icon = ...` dans `Main.kt` | @@ -25,7 +25,7 @@ Décliner le logo maître (`docs/branding/logo.svg`, spec 2026-08-31-logo-design ### 3.1 Variantes SVG sources (`docs/branding/`) - `icon-square.svg` : viewBox `0 0 128 128` ; rect plein `(0,0,128,128)` sans arrondi fill `#1D3557` ; tuiles et glyphes aux positions exactes du master → iOS (masque arrondi appliqué par l'OS) et background Android -- `icon-foreground.svg` : viewBox `0 0 108 108` ; groupe tuiles+glyphes du master, scale `66/92 ≈ 0.7174` centré, translaté au centre `(54,54)` → couche foreground +- `icon-foreground.svg` : viewBox `0 0 108 108` ; groupe tuiles+glyphes du master, scale `66/(92·√2) ≈ 0.5073` (la diagonale de la grille 92×92 du master doit tenir dans le cercle de sûreté de 66 dp de diamètre), translaté au centre `(54,54)` → couche foreground - `icon-monochrome.svg` : géométrie du foreground, seules les 4 tuiles en fill `#FFFFFF`, fond transparent → couche monochrome ### 3.2 Assets Android (remplacement in situ, mêmes chemins)