Bloc 95 — 📱 Manifeste PWA + icônes mobiles (audit SEO F1) - #120
Merged
Merged
Conversation
ML-Helper devient installable sur l'écran d'accueil d'un téléphone : icône, nom court, ouverture en plein écran sans barre d'adresse. Icônes générées depuis l'image source fournie (1254×1254) avec sharp : src/app/icon.png (192×192) et src/app/apple-icon.png (512×512), les deux conventions de nommage que Next.js détecte pour émettre <link rel="icon"> et <link rel="apple-touch-icon">. Encodage en palette plutôt qu'en RGB : 136 Ko au lieu de 475 Ko pour le 512, pour une erreur de quantification mesurée à 2,0/255 (RMSE, soit moins de 1 %) et vérifiée à l'œil — aucune bande visible dans les dégradés dorés ni sur le fond bleu nuit. Les icônes ne sont volontairement PAS déclarées `maskable` : le blason occupe ~83 % du carré, au-delà de la zone sûre de 80 % à laquelle Android recadre une icône maskable, ce qui en rognerait les bords. Couleurs reprises des jetons du site, pas inventées : background_color = --bg (#1b2029), le fond du thème sombre, pour que l'écran de démarrage corresponde à ce qui s'affiche juste après ; theme_color = --accent (#8b6bb8, résolu à travers son alias var(--violet)). Un test les compare à globals.css, donc un changement de palette ne peut pas laisser ce fichier en arrière — contre-vérifié en désaccordant la valeur. start_url vaut "/" et non "/fr" : src/proxy.ts redirige la racine nue vers la langue du visiteur, donc l'app installée n'est pas figée sur une locale au moment de l'installation.⚠️ Écart au brief, vérifié : le brief indique que Next.js sert src/app/manifest.ts en /manifest.json. C'est /manifest.webmanifest — la route confirmée par la sortie de build (seul un fichier statique manifest.json serait servi en .json). Sans conséquence sur l'installabilité : Next injecte <link rel="manifest"> vers cette route et les navigateurs suivent ce lien au lieu de deviner un nom de fichier. Le test e2e lit d'ailleurs l'URL depuis la balise plutôt que de la coder en dur, ce qui lui fait aussi remarquer un déplacement de route. Tests : 6 tests unitaires (champs, couleurs liées aux jetons CSS, tailles PNG lues dans le chunk IHDR, absence de maskable) et 3 tests e2e (balise link + manifeste servi et valide, les 2 icônes servies aux dimensions qu'elles annoncent, apple-touch-icon exposée). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJgsDSfsCbbn8cochFGwe2
There was a problem hiding this comment.
đź’ˇ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e222670846
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Le `name` du manifeste est le texte que l'invite d'installation affiche — donc du texte visible par l'utilisateur, qui doit passer par next-intl (AGENTS.md) au lieu d'être figé en français pour les 5 langues. Le nom réutilise `Public.meta.siteTitle`, le nom du site déjà traduit dans les 5 fichiers de messages — pas de nouvelle clé quasi-dupliquée. La convention de fichier `app/manifest` n'existe qu'à la racine et produit un document statique unique, qui ne peut pas varier par visiteur : elle garde donc le libellé anglais (le repli documenté) et couvre les routes non préfixées (/admin, /login). Chaque langue sert le sien via une route `/[locale]/manifest.webmanifest` prérendue, vers laquelle le layout de locale pointe par `generateMetadata` — vérifié sur le HTML rendu en e2e. Tout ce qui ne dépend pas de la langue vient d'un seul `buildWebManifest`, pour que les deux entrées ne divergent pas. Tests : le mock next-intl des tests unitaires lit les vrais fichiers `messages/*.json`, donc les assertions comparent le nom servi aux vraies traductions ; un nom figé fait rougir 6 tests (vérifié en le recodant en dur). E2E : chaque locale lie bien son propre manifeste et trois locales donnent trois noms différents. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJgsDSfsCbbn8cochFGwe2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ce qui change
ML-Helper devient installable sur l'écran d'accueil d'un téléphone : icône, nom court, ouverture en plein écran sans barre d'adresse. Reprend le point F1 de l'audit SEO (Bloc 91), volontairement différé jusqu'ici.
src/app/icon.png— 192×192 (23 Ko)src/app/apple-icon.png— 512×512 (136 Ko)src/app/manifest.ts— servi en/manifest.webmanifestsrc/app/[locale]/manifest.webmanifest/route.ts— un manifeste par langue (voir la revue Codex plus bas)src/lib/web-manifest.ts— la partie du manifeste identique dans les 5 languesLes deux noms de fichiers d'icônes sont les conventions que Next.js détecte pour émettre
<link rel="icon">et<link rel="apple-touch-icon">.1. La route. Le brief indique que
src/app/manifest.tsest servi en/manifest.json. C'est/manifest.webmanifest— route confirmée par la sortie de build. Seul un fichier statiquemanifest.jsonserait servi en.json; la convention.tsémet toujours.webmanifest.Sans conséquence sur l'installabilité : Next injecte
<link rel="manifest">vers cette route, et les navigateurs suivent ce lien au lieu de deviner un nom de fichier. Le test e2e lit d'ailleurs l'URL depuis la balise plutôt que de la coder en dur — ce qui lui fait aussi remarquer un éventuel déplacement de route. Dis-moi si tu veux quand même un alias/manifest.json.2. Le nom affiché, suite à la revue Codex. Le
namedu brief (« ML-Helper — Outils Million Lords ») était figé en français pour les 5 langues, alors que c'est le libellé que l'invite d'installation affiche — donc du texte visible qui doit passer par next-intl (AGENTS.md). Il vient maintenant dePublic.meta.siteTitle, le nom du site déjà traduit dans les 5 fichiers de messages : en FR l'app s'appelle donc « ML-Helper — Outils et guides Million Lords ». J'ai préféré réutiliser cette clé plutôt qu'en créer une seconde quasi identique à traduire 5 fois — dis-moi si tu veux exactement le libellé du brief à la place.Concrètement : la convention
app/manifestn'existe qu'à la racine et produit un document statique unique, qui ne peut pas varier par visiteur. Elle garde donc le libellé anglais (le repli documenté par la même ligne d'AGENTS.md) et couvre les routes non préfixées (/admin,/login) ; chaque langue sert le sien via/[locale]/manifest.webmanifest, prérendu, vers lequelgenerateMetadatadu layout de locale fait pointer son<link rel="manifest">. Tout ce qui ne dépend pas de la langue vient d'un seulbuildWebManifest, pour que les deux entrées ne divergent pas.Décisions prises
Encodage en palette plutôt qu'en RGB. 136 Ko au lieu de 475 Ko pour le 512 — non négligeable pour un fichier récupéré au moment de l'installation. Erreur de quantification mesurée à RMSE 2,0/255 (moins de 1 %), puis vérifiée à l'œil : aucune bande visible dans les dégradés dorés ni sur le fond bleu nuit.
Pas de
purpose: "maskable". Le blason occupe ~83 % du carré, au-delà de la zone sûre de 80 % à laquelle Android recadre une icône maskable — le déclarer en rognerait les bords. Un test épingle ce choix pour qu'il ne soit pas ajouté par réflexe plus tard.Couleurs reprises des jetons, pas inventées.
background_color=--bg(#1b2029), le fond du thème sombre, pour que l'écran de démarrage corresponde à ce qui s'affiche juste après ;theme_color=--accent(#8b6bb8, résolu à travers son aliasvar(--violet)), qui teinte l'interface du navigateur. Si tu préfères une barre d'état qui se fond dans le fond sombre plutôt qu'une teinte violette, c'est une ligne à changer.start_url: "/"et non/fr.src/proxy.tsredirige la racine nue vers la langue du visiteur : l'app installée n'est donc pas figée sur la locale du jour de l'installation.Tests
15 tests unitaires (
src/app/manifest.test.ts) : champs attendus, tailles PNG lues dans le chunk IHDR des fichiers réels (la taille annoncée est comparée aux octets, pas à elle-même), absence demaskable, couleurs comparées aux jetons deglobals.css(un changement de palette ne peut pas laisser ce fichier en arrière), et le manifeste de chacune des 5 locales. Le mock next-intl lit les vraismessages/*.json, donc le nom servi est comparé aux vraies traductions. Contre-vérifié dans les deux sens : désaccordertheme_colorrougit ; recoder un nom en dur rougit 6 tests.4 tests e2e : balise
<link rel="manifest">présente et manifeste servi valide (content-typemanifest+jsoncompris), chaque locale lie le sien et trois locales donnent trois noms différents, les 2 icônes servies aux dimensions qu'elles annoncent (dimensions relues dans les octets servis), etapple-touch-iconexposée pour iOS.Validation
pnpm lint+ typecheck : proprespnpm test: 1261 tests vertspnpm build: succès — routes/manifest.webmanifest, les 5/[locale]/manifest.webmanifestprérendues,/icon.png,/apple-icon.pngconfirméespnpm test:e2e: 85/85 verts (3,9 min)🤖 Generated with Claude Code
https://claude.ai/code/session_01HJgsDSfsCbbn8cochFGwe2
Generated by Claude Code