From 7150d3bcae276326331bbc6042ec8a8a2e36838d Mon Sep 17 00:00:00 2001 From: mathieu Date: Thu, 20 Aug 2026 21:15:38 +0200 Subject: [PATCH] Documente le lot H dans CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Les seize retours du 2026-08-20, avec ce que chacun a appris. Deux décisions actées sont explicitement renversées, et leur raisonnement d'origine conservé plutôt qu'effacé : la barre d'onglets en bas (juste sur le pouce, mais sans état de repli) et le fichier logo-bandeau.svg séparé (qu'un cache pouvait ne pas servir). Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 287 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 264 insertions(+), 23 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fa0d2c4..558da19 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1482,12 +1482,14 @@ ouverte dans IDEES.md. Vérifié en exécution sur `9772466671438` : la BnF nomme « Médor », la fiche est créée avec son ISSN, deux numéros s'y ajoutent, et **rescanner le même code retombe sur la même fiche**. -### Un sixième onglet, mesuré +### ~~Un sixième onglet, mesuré~~ — caduc depuis le 2026-08-20 -`Catalogue / Auteurs / Séries / Revues / Prêts / Envies`. À 320 px — le plus étroit des -téléphones réalistes — les six libellés occupent **312 px sans troncature**, huit de marge. -⚠️ C'est la limite : un septième onglet, ou un libellé plus long que « Catalogue », imposera de -regrouper plutôt que d'ajouter. +`Catalogue / Auteurs / Séries / Revues / Prêts / Envies`. À 320 px, les six libellés occupaient +**312 px sans troncature**, huit de marge — c'était la limite, et elle a été atteinte. + +⚠️ **La barre d'onglets n'existe plus** : la navigation est passée en haut, dans un menu qui se +déploie. Voir « Le menu est en haut ». La mesure ci-dessus reste instructive pour une seule +raison : elle montre qu'une rangée qui « tient tout juste » ne tient en réalité pas. ## Sagas et cycles — migration `SagasEtCycles` (2026-08-19) @@ -1558,10 +1560,11 @@ sans quoi une coupure juste après un ajout de tome rendrait la série d'avant. ### L'écran, et ce qu'il refuse de faire -- **Un cinquième onglet.** Vérifié à 320 px : les cinq libellés tiennent sans troncature - (258 px sur 320), au prix d'un `font-size` passé de 0.85 à 0.8 rem. +- **Un cinquième onglet.** (Caduc : la barre d'onglets a disparu le 2026-08-20.) - **Des flèches, pas de glisser-déposer** : le drag & drop HTML5 ne fonctionne pas au doigt, et c'est sur téléphone qu'on consulte une saga. Même décision que pour la liste d'envies. + ⚠️ Depuis le 2026-08-20, ces flèches ne vivent plus que sur `/series/{id}/ordre` — voir + « L'ordre de lecture a son propre écran ». - **Le titre affiché est celui du livre quand il est rattaché**, le titre saisi restant le filet. Rattacher le mauvais livre se voit donc immédiatement — et « Détacher » restitue le nom du tome. - **Aucune détection automatique** de série depuis un titre (« Tome 3 »), aucune source tierce. @@ -1619,28 +1622,28 @@ sinon on créerait des fiches sans format en croyant que « rien d'affiché = ph acquis sans code particulier (`Format.Physique` vaut 0, valeur par défaut de l'énumération) — mais si l'énumération change d'ordre un jour, ce comportement tombe. -### La navigation tient dans quatre onglets, en bas de l'écran +### La navigation est une liste de destinations, pas une rangée d'onglets -`MainLayout` porte une barre fixe **Catalogue / Auteurs / Prêts / Envies**. Elle remplace les -listes de liens que chaque page traînait dans sa barre d'actions : cinq boutons y passaient sur -deux ou trois lignes sur un téléphone, et « Envies » ne figurait pas partout. +`MainLayout` porte les six destinations **Catalogue / Auteurs / Séries / Revues / Prêts / +Envies**. Elles remplacent les listes de liens que chaque page traînait dans sa barre d'actions : +cinq boutons y passaient sur deux ou trois lignes sur un téléphone, et « Envies » ne figurait pas +partout. Conséquence tenue partout : **`.actions-flottantes` ne porte plus que des *actions***. Tout lien -qui doublonnait exactement une destination d'onglet a été retiré — « Retour au catalogue », -« Retour aux auteurs », « Ma liste d'envies ». Ce qui reste est ce qu'aucun onglet ne sait faire -(« Ses livres chez vous », qui est un catalogue *restreint*). - -⚠️ **En bas, pas sous le bandeau** : le pouce atteint le bas de l'écran. Les deux barres fixes -coexistent grâce à `--mb-onglets-hauteur`, partagée entre `MainLayout.razor.css` et `app.css` — -sans elle, la barre d'actions recouvrirait les onglets. Vérifié à 375 px : onglets 770-812, -actions 706-770, aucun chevauchement. +qui doublonnait exactement une destination du menu a été retiré — « Retour au catalogue », +« Retour aux auteurs », « Ma liste d'envies ». Ce qui reste est ce qu'aucune destination ne sait +faire (« Ses livres chez vous », qui est un catalogue *restreint*). ⚠️ `NavLinkMatch.All` sur « Catalogue » est **obligatoire** : son `href` est la racine, et sans -cela l'onglet resterait allumé sur les quatre écrans. +cela l'entrée resterait allumée sur tous les écrans. -Les onglets **restent actifs hors-ligne** : les quatre écrans se consultent depuis leurs +Les entrées **restent actives hors-ligne** : les six écrans se consultent depuis leurs instantanés. Ce sont les écritures qui se désactivent, jamais la navigation. +⚠️ **La position, en revanche, a changé le 2026-08-20** — voir « Le menu est en haut », qui +renverse le « en bas, le pouce atteint le bas de l'écran » écrit ici le 2026-08-18. La variable +`--mb-onglets-hauteur` qui faisait cohabiter les deux barres fixes n'existe plus. + ### Les filtres du catalogue se replient derrière un bouton Deux rangées de segments occupaient en permanence le haut de l'écran pour un réglage qu'on @@ -1724,8 +1727,9 @@ classe, pour ne pas diverger une troisième fois. `favicon.png` / `icon-192.png` / `icon-512.png` étaient encore le logo « @ » violet du template `dotnet new blazorwasm`. Remplacés par un pictogramme de livre ouvert (deux pages en V, tranche centrale blanche), dans le bleu d'accent existant (`#1b3a5c`) — généré par script (Pillow), pas -par un outil de génération d'images IA, pour rester un simple dessin géométrique. Un petit -`logo-bandeau.svg` (blanc, net à toute résolution) accompagne « MaBibli » dans le bandeau. +par un outil de génération d'images IA, pour rester un simple dessin géométrique. Le même dessin +accompagne « MaBibli » dans le bandeau — ⚠️ **en SVG *en ligne* depuis le 2026-08-20**, et non +plus dans un `logo-bandeau.svg` séparé : voir « L'icône cassée du bandeau ». `mabibli_ynh/logo.png` (256×256, fond transparent — convention du catalogue d'applications YunoHost) reprend le même dessin. @@ -2143,3 +2147,240 @@ nouveau script, et les 441 tests. ⚠️ **Un appareil déjà dans l'état dépareillé ne se répare pas tout seul** : son cache mélangé précède le correctif. Il faut vider les données du site (ou désinstaller puis réinstaller la PWA) **une fois**. Le correctif empêche d'y retomber, il ne défait pas ce qui est déjà en place. + +## Lot H — retours d'usage du 2026-08-20 (5ᵉ série) + +Seize points remontés en usage sur l'application installée. Ils se rangent en cinq +familles ; l'ordre ci-dessous est celui du traitement, pas celui de la remontée. + +### Le menu est en haut — décision de 2026-08-18 renversée + +Constat de l'utilisateur : la barre d'onglets du bas « s'affiche toujours très mal ». + +⚠️ **Le raisonnement du 2026-08-18 reste juste, et il ne suffisait pas.** « Le pouce +atteint le bas de l'écran » est vrai ; mais une barre qui ne s'affiche pas correctement ne +se touche pas du tout. Ce qui manquait n'était pas la position, c'était **un état de +repli** : six onglets en `flex: 1 1 0` sur une barre fixe n'ont aucun comportement de +secours — dès que la place manque, ou que la feuille de style scopée n'est pas celle +attendue, les six libellés se collent, se tronquent ou s'empilent sans mise en forme. + +Le remplacement suit le gabarit **Blazor par défaut**, pour cette raison précise : sans la +moindre feuille de style, il reste **une suite de liens lisibles les uns sous les autres**. + +| Largeur | Menu | Bascule | +|---|---|---| +| < 40 rem (téléphone) | masqué, se déploie au clic | visible | +| ≥ 40 rem | rangée horizontale centrée sous le bandeau | masquée | + +Le point de rupture est à **40 rem** et non aux 48 rem du reste de la feuille : six +libellés courts tiennent bien avant que la grille de cartes ne s'élargisse, et les cacher +derrière une bascule sur une tablette serait un clic de trop. Vérifié en exécution à +375 px (menu `none`, bascule `flex`) et à 1280 px (menu `row`, bascule `none`), sans +débordement horizontal ni dans un cas ni dans l'autre. + +⚠️ Le menu **se referme sur `LocationChanged`** : `NavLink` ne referme rien de lui-même, et +un clic sur « Auteurs » laissait les six entrées empilées au-dessus de la liste atteinte. + +Conséquence sur `app.css` : `--mb-onglets-hauteur` **a disparu**, et `.actions-flottantes` +se pose directement en `bottom: 0`. Plus rien ne l'accompagne en bas de l'écran. + +### Un retour explicite dans le bandeau + +« Le déplacement est foireux » : d'un écran profond (bibliographie, fiche de tome, ajout +d'envie), il fallait deviner quelle destination du menu ramenait en arrière. + +Le retour passe par l'**historique du navigateur**, jamais par une destination calculée : +un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le +catalogue, une bibliographie, une série ou une recherche. + +⚠️ `history.back()` **seul ne suffit pas** : ouverte depuis l'écran d'accueil du téléphone, +la PWA démarre sur une pile d'un seul cran et « retour » **sortirait de l'application**. +D'où `js/navigation.js`, qui teste `history.length` — sans équivalent côté C# — et retombe +sur le catalogue. + +### L'icône cassée du bandeau + +`logo-bandeau.svg` était un fichier **séparé**, servi (ou non) indépendamment de +l'application : un appareil dont le cache précédait son ajout du 2026-08-20 recevait un +404, d'où l'icône cassée en haut à gauche. Le pictogramme est désormais **en ligne dans le +balisage** — un SVG en ligne ne peut pas manquer. Le fichier a été supprimé. + +C'est la même famille de défaut que celle du lot A1 et de la mise à jour dépareillée : un +fichier statique à nom stable qu'un cache peut servir — ou ne pas servir — indépendamment +du reste. + +### ⚠️ La bibliographie rappelait la BnF à chaque clic + +Symptôme : « masquer/afficher prend énormément de temps, ainsi que le bouton Je le veux ». + +Cause, et elle est instructive : chaque écriture appelait `ChargerAsync()`, c'est-à-dire +une **interrogation complète de la BnF** (≈1 s par page, jusqu'à **dix pages** pour les +nouveautés, qui ne sont pas mises en cache) suivie d'une **relecture de tout le catalogue**. +`ModifierBibliographieAsync` faisait de surcroît `_bibliographies.Clear()`, ce qui vidait +le cache de session mis en place au lot D. + +**L'écran attendait une source distante pour apprendre un drapeau qu'il connaissait déjà.** +Masquer une œuvre ou l'ajouter à ses envies ne change *rien* à ce que la BnF sait de +l'auteur : seuls basculent des drapeaux **personnels**, que le client peut poser lui-même. + +La correction est en deux endroits, et les deux sont nécessaires : + +| Où | Quoi | +|---|---| +| `ServiceLivresApi.PatcherOeuvres` | corrige les bibliographies **en cache**, au lieu de les vider | +| `Bibliographie.MettreAJourOeuvre` | corrige la copie **que l'écran tient en main** | + +⚠️ Le second n'est pas redondant : les **nouveautés ne sont pas dans le cache**, et c'est +précisément le cas le plus lent. + +Mesuré en exécution après correction : **181 ms** pour « Je le veux », **226 ms** pour un +masquage groupé de deux œuvres — contre un rechargement BnF complet auparavant. + +### La bibliographie est un écran où l'on coche + +- **Case à gauche du titre, ligne centrée verticalement, boutons groupés à droite.** + Alignés en haut, la case flottait au-dessus du texte et les boutons pendaient sous lui. + Vérifié : case 16–46 px, actions 279–359 px, tous centrés sur la même ligne à 375 px. +- **La case est offerte sur TOUTE œuvre visible**, pas seulement sur celles à découvrir : + masquer en lot suppose de pouvoir cocher ce qu'on possède déjà. +- **La barre de sélection est en haut, et collante.** On coche en descendant : une barre + en bas obligeait soit à redescendre pour valider, soit — pire — recouvrait les dernières + lignes qu'on cherchait justement à cocher. Elle porte « Ajouter aux envies (n) », + « Masquer (n) », « Réafficher (n) » et « Tout décocher », chaque compteur ne comptant + que ce que son bouton peut réellement traiter. +- **« Nouveautés » et « Ses livres chez vous » remontent à côté du filtre** : ce sont trois + façons de restreindre la même liste, et « Nouveautés » est ce qu'on veut *en ouvrant* + l'écran, pas après avoir parcouru cinquante lignes. Le composant `BasculeBibliographie` + évite d'en tenir deux copies (liste garnie, liste vide). + +### Auteurs : chercher, trier, et aligner + +- **Recherche et tri en mémoire, sans nouvel appel.** La liste des auteurs est déjà chargée + en entier — c'est elle qui sert l'instantané hors-ligne — et la filtrer côté serveur + aurait rendu la page inutilisable sans réseau. Recherche par **sous-chaîne du nom + normalisé** (comme le catalogue), tri **ordinal sur la forme normalisée** (comme + `FiltreLivresLocal`), pour qu'un appareil ne classe pas « Éluard » autrement qu'un autre. +- **Deux tris, et pas un troisième** : par nom (« où est untel ? ») et par nombre de livres + (« qui ai-je le plus ? »). +- **Trois colonnes dont une seule est élastique** : compteur à gauche sur **2,5 rem** + (trois chiffres — personne ne possède mille livres d'un même auteur), nom au milieu, + actions à droite. ⚠️ C'est la largeur **figée** des deux colonnes extrêmes qui fait tout + l'intérêt : sans elle, chaque ligne plaçait son compteur et ses boutons ailleurs, et + l'œil devait relire chaque ligne entière. Vérifié : bord droit du compteur à 56 px et + colonne d'actions à 152–359 px, **identiques sur toutes les lignes**. +- Au passage, `champ-texte` du champ de renommage n'existait dans aucune feuille de style ; + il est devenu `champ-saisie`. + +### Filtre de prêt au catalogue + +`CritereLivres.Prete` : `true` = ce qui est dehors, `false` = ce qui est à la maison, +`null` = tout. ⚠️ **Commun au foyer**, contrairement au statut de lecture : aucune identité +n'entre dans son évaluation. L'écran « Prêts » répondait déjà à « qu'est-ce qui est +dehors ? » ; ce filtre sert l'autre moitié — « qu'est-ce que j'ai réellement sous la +main ? », avant de promettre un livre à quelqu'un. + +⚠️ Toute dimension de filtre s'ajoute **aux deux implémentations** et au test qui les +confronte. Quatre jeux de critères ajoutés, et surtout un livre au **prêt clos** dans le +jeu de données : c'est exactement là que le serveur (`DateRetour == null`) et le client +(`PreteA is not null`) auraient pu diverger en silence. + +La ligne se replie derrière « Filtrer », compte dans le compteur du bouton, et **disparaît +tant que rien n'est prêté** — mêmes règles et mêmes précautions que les formats et les +types (déduction sur un chargement `EstSansCritere` uniquement). + +### Les exports portent le rang d'envie + +Le CSV suivait déjà l'ordre choisi, mais un ordre **implicite se perd au premier tri par +titre** dans un tableur : une colonne « Rang » l'ouvre désormais. + +⚠️ Le `.txt` en avait bien plus besoin : son **groupement par auteur détruit l'ordre** +d'envie — c'est le prix, assumé, d'une liste rangée comme une librairie. Le rang est donc +réinscrit devant chaque titre (` - [3] Titre`), sous une ligne qui l'explique. + +Le rang imprimé est la **position dans la liste reçue**, pas la valeur brute de la colonne +`Rang` : celle-ci peut comporter des trous (une suppression ne renumérote pas), et ce qu'on +veut lire est « troisième de ma liste ». + +### ⚠️ Les couvertures des envies : le défaut n'était pas où on le cherchait + +« Dans la liste d'envies, aucune image n'est trouvée, ce qui est bizarre. » + +Ce n'était ni le composant `Couverture`, ni OpenLibrary : **seul l'écran « ajouter une +envie » remplissait `CoverUrl`**. Or la plupart des envies arrivent par la bibliographie +d'un auteur ou par un tome manquant d'une série — deux chemins qui transmettent bien un +ISBN, mais aucune image. + +Le repli est posé **à la lecture** (`ServiceSouhaits.Projeter`), et non à l'écriture : il +vaut alors aussi pour les envies **déjà enregistrées**, sans migration ni rattrapage. La +règle « sans ISBN, pas de couverture, et on n'en invente pas » est intacte — on n'invente +rien, on applique la formule OpenLibrary habituelle à un ISBN qu'on possède déjà. + +### L'arbre des séries ne montre que la descendance + +« Quand je parle de tree, je pensais vraiment à quelque chose de beaucoup plus simple. » + +L'arbre dépliait la **liste complète des tomes** de chaque nœud, cartes de livres +comprises : un cycle de quatre séries occupait plusieurs écrans, et l'on ne voyait plus ce +qu'un arbre sert à voir — **qui contient quoi**. Il ne porte plus que le titre, +l'avancement cumulé et le lien vers la fiche ; les tomes ont leur écran. + +⚠️ **Corollaire : tout est déplié par défaut**, et l'on replie ce dont on ne veut pas. +L'inverse obligeait à ouvrir chaque nœud pour découvrir s'il contenait quelque chose, +c'est-à-dire à faire à la main le travail de l'arbre. + +`/series` **ne charge plus le catalogue entier** : l'arbre n'en a plus l'usage. + +Vérifié en exécution sur *La Légende de Drizzt* : les trois lignes tiennent à 375 px, +indentées de 16 px et 42 px, et le cycle totalise bien « 0 sur 5 tomes » (3 + 2). + +### L'ordre de lecture a son propre écran + +« Je ne veux pas pouvoir modifier l'ordre sans le vouloir. » + +Nouvelle route **`/series/{id}/ordre`**, sur le modèle exact de `/souhaits/ordre`. Les +flèches — tomes **et** sous-séries d'un cycle — n'existent plus qu'ici : offertes en +consultation, elles se déclenchaient au défilement du pouce, et l'ordre de lecture d'une +saga changeait sans qu'on l'ait voulu. + +⚠️ Le mode est dans l'**adresse**, pas dans un booléen interne : convention du projet depuis +la fiche livre, et elle fait du bouton « retour » du navigateur une sortie naturelle. Les +trois routes partageant le même paramètre, le composant reste abonné à `LocationChanged`. + +L'écran d'ordre **ne fait que réordonner** : ni actions par tome, ni ajout de tome. Le +numéro de rang, lui, reste visible en consultation — c'est une information, pas une action. + +Le lien « Changer l'ordre » n'apparaît qu'à partir de deux tomes ou deux sous-séries : +réordonner un élément unique n'a pas de sens. + +### Une seule entrée d'ajout : « Ajouter un ouvrage » (décidé avec l'utilisateur) + +Question posée telle quelle par l'utilisateur : refaire une page « ajouter une revue » +calquée sur celle des livres, ou renommer « livre » en « ouvrage » et regrouper ? +**Tranché : une entrée unique**, atteinte depuis le catalogue **et** depuis les revues. + +Le geste réel est « j'ai un truc avec un code-barres », pas « je vais cataloguer un +livre » : obliger à savoir d'avance ce qu'on tient était un détour, d'autant que le +décodage distingue déjà un `978`/`979` d'un `977`. + +⚠️ **Le MODÈLE ne bouge pas, et il ne doit pas bouger** : une revue reste une `Revue`, +jamais un `Livre`. Fondre les deux tables obligerait à répéter « et qui n'est pas une +revue » à chaque lecture du catalogue, des compteurs, de la détection de doublons, des +séries et des bibliographies par auteur. C'est l'ergonomie qui est regroupée, pas le +schéma. + +| Route | Rôle | +|---|---| +| `/ajout` (alias `/ajout/isbn`) | scan et saisie de code, pour livre comme pour magazine | +| `/ajout/manuel` | saisie manuelle d'un **livre** | +| `/revues/ajout` | saisie manuelle d'une **revue** (titre, ISSN, éditeur) | + +⚠️ La saisie manuelle est le **seul** endroit où l'entrée unique doit reposer la question : +sans code à lire, rien ne peut trancher à la place de l'utilisateur. D'où deux boutons +distincts, et non un formulaire à bascule. + +Le champ « nom d'une nouvelle revue » a quitté le milieu de `/revues`. `/revues/ajout` +enchaîne sur la **fiche** de la revue plutôt que de revenir à la liste : le geste n'est +jamais « créer une revue », c'est toujours « ranger un numéro ». + +Vérifié en exécution : `9772466671438` saisi depuis « Ajouter un ouvrage » nomme toujours +« Médor » et propose d'ouvrir sa fiche.