Commit Graph
65 Commits
Author SHA1 Message Date
mathieuandClaude Opus 5 ef63837a3f Note le lot N : les couvertures lentes au rechargement, et pourquoi ce n'est pas l'URL
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:02:55 +02:00
mathieuandClaude Opus 5 f846cab820 Note les lots J à M : scan manquant, photo de couverture, ISSN, attente visible
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:56:18 +02:00
mathieuandClaude Opus 5 c228e79393 Note le lot I : tout cocher, et la couverture d'une œuvre non possédée
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:37:58 +02:00
mathieuandClaude Opus 5 b16e70913f Relaie les couvertures en même origine, pour les mettre en cache hors-ligne
Le cache hors-ligne des couvertures ne marchait que pour OpenLibrary : il lit
les octets par fetch(), donc exige un en-tête CORS, alors que le formulaire
livre accepte n'importe quelle URL. Ces images s'affichaient (une <img> n'a
que faire du CORS) sans jamais pouvoir être rangées — et le fetch repartait à
chaque affichage puisque rien n'était stocké.

GET /api/couvertures relaie l'image depuis notre serveur. Un proxy est une
surface SSRF : il est borné par deux verrous indépendants — l'URL doit déjà
exister en base comme couverture, et la connexion ne s'ouvre que vers une
adresse publiquement routable. Ce second verrou vit dans le ConnectCallback,
pas dans une pré-vérification DNS, ce qui ferme aussi le DNS rebinding — et
c'est ce qui permet de suivre les redirections, indispensables puisque
covers.openlibrary.org répond 302.

Tout refus répond 404 : distinguer les cas ferait du point d'entrée un oracle
sur les URL connues et sur le réseau du serveur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:14:08 +02:00
mathieuandClaude Opus 5 e74d20adfa Demande le manifeste PWA avec le cookie de session
Le navigateur demande `manifest.webmanifest` sans cookie par défaut : SSOwat
ne reconnaît aucune session et redirige vers le portail YunoHost, qui est une
autre origine — la réponse n'a pas d'Access-Control-Allow-Origin et le
manifeste est bloqué.

L'application fonctionnait quand même, ce qui rendait le défaut discret : ce
qui tombait, c'est ce que le manifeste décide à l'INSTALLATION (nom, icônes,
start_url, mode standalone).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:56:43 +02:00
mathieuandClaude Opus 5 345422eda2 Habille enfin les liens du menu : NavLink échappe à l'isolation CSS
Les six entrées restaient en bleu souligné par défaut : l'isolation CSS de
Blazor ne pose son attribut de portée que sur le balisage du composant
courant, jamais sur ce que rend un composant enfant. NavLink étant un
composant, `.menu-lien` déclaré dans MainLayout.razor.css ne correspondait à
aucun élément — alors que `.menu` et `.bandeau`, du balisage propre, étaient
bien habillés.

Les règles du menu passent en feuille globale plutôt qu'en `::deep` : la
navigation ne dépend plus du bundle d'isolation ni de son cache, qui est
précisément le fichier déjà vu servi périmé ou absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:53:41 +02:00
mathieuandClaude Opus 5 7150d3bcae Documente le lot H dans CLAUDE.md
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 <noreply@anthropic.com>
2026-08-20 21:15:38 +02:00
mathieuandClaude Opus 5 0c4babd7b9 Une seule entrée d'ajout : « Ajouter un ouvrage »
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 même écran est atteint depuis le
catalogue et depuis les revues.

⚠️ Le MODÈLE ne bouge pas : une revue reste une Revue, jamais un Livre. Fondre les deux
tables aurait obligé à répéter « et qui n'est pas une revue » à chaque lecture du
catalogue, des compteurs, des doublons, des séries et des bibliographies.

- /ajout (alias /ajout/isbn) : « Ajouter un ouvrage », deux saisies manuelles distinctes
  puisque, sans code à lire, il faut bien trancher soi-même.
- Nouvelle page /revues/ajout : titre, ISSN et éditeur, puis la fiche de la revue. Elle
  remplace le champ posé au milieu de la liste des revues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:13:01 +02:00
mathieuandClaude Opus 5 bd4fea2638 Arbre des séries dépouillé, et ordre de lecture derrière son propre écran
- L'arbre ne montre plus que la descendance : titre, avancement, lien. Il dépliait
  auparavant la liste complète des tomes de chaque nœud, cartes de livres comprises —
  un cycle occupait plusieurs écrans et l'on ne voyait plus qui contient quoi. Corollaire :
  tout est déplié par défaut, et l'on replie ce dont on ne veut pas.
- /series ne charge plus le catalogue entier : l'arbre n'en a plus l'usage.
- ⚠️ Nouvelle route /series/{id}/ordre, sur le modèle de /souhaits/ordre. Les flèches
  n'existent plus qu'ici : 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 ordre ne fait QUE réordonner : les actions par tome et l'ajout de tome n'y
  figurent pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:45:14 +02:00
mathieuandClaude Opus 5 4fd67e4e3b Auteurs cherchables et triables, filtre de prêt, rang dans les exports, couvertures d'envies
Auteurs
- Recherche et tri (par nom, ou du plus présent au moins présent), en mémoire : la liste
  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.
- Trois colonnes dont une seule est élastique : compteur à gauche sur une largeur figée,
  nom au milieu, actions à droite. Les compteurs et les boutons s'alignent alors d'une
  ligne à l'autre au lieu de suivre la longueur du nom.

Catalogue
- Filtre « À la maison / Prêtés », posé dans les deux implémentations (serveur et
  hors-ligne) et dans le test qui les confronte, avec un cas de prêt CLOS — c'est
  exactement là que les deux auraient pu diverger en silence.
- La ligne disparaît tant que rien n'est prêté, comme celles des formats et des types.

Envies
- ⚠️ Les couvertures manquaient parce que seul l'écran « ajouter une envie » remplissait
  CoverUrl : la bibliographie et les tomes manquants transmettent un ISBN mais aucune
  image. Le repli est posé à la LECTURE, donc il vaut aussi pour les envies déjà
  enregistrées, sans migration.
- Les exports .txt et .csv portent le rang d'envie. Le .txt en a le plus besoin : son
  groupement par auteur détruit précisément cet ordre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:42:13 +02:00
mathieuandClaude Opus 5 83089d7479 Menu en haut, retour explicite, et bibliographie qui ne rappelle plus la BnF
Navigation
- La barre d'onglets du bas devient un menu en haut, sur le repli du gabarit Blazor :
  bascule sur téléphone, rangée sur PC. Cela renverse la décision du 2026-08-18 (le
  pouce atteint le bas de l'écran) : le raisonnement reste juste, mais il ne vaut rien
  face à une barre qui s'affiche mal. Le point tenu est qu'il existe désormais un état
  de repli — sans feuille de style, il reste une liste de liens lisible.
- Bouton « retour » dans le bandeau, sur tous les écrans, appuyé sur l'historique du
  navigateur, avec repli sur le catalogue quand la pile est vide (PWA ouverte depuis
  l'écran d'accueil).
- Le pictogramme de la marque passe en SVG en ligne : le fichier logo-bandeau.svg
  pouvait manquer sur un appareil dont le cache précédait son ajout, d'où l'icône cassée.

Bibliographie
- Case à cocher à gauche du titre, ligne centrée verticalement, boutons groupés à droite.
- Barre de sélection collante EN HAUT, avec masquage groupé et « tout décocher ».
- « Nouveautés » et « Ses livres chez vous » remontent à côté du filtre.
- ⚠️ « Je le veux » et « Masquer » ne rechargent plus rien : ils basculent un drapeau
  personnel que le client connaît déjà. Auparavant chaque clic vidait le cache des
  bibliographies et relançait la BnF (une seconde par page, jusqu'à dix pages pour les
  nouveautés, qui ne sont pas mises en cache) puis relisait tout le catalogue.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:35:39 +02:00
mathieuandClaude Opus 5 c256394c77 Appliquer la mise a jour au demarrage et fermer les fuites de cache
Sur l'application installee sur telephone, le bandeau restait sans effet et
le chargement se figeait a 100 %. La capture montrait le bandeau SANS son
CSS, alors que le script et sa regle sont arrivés dans le meme commit : ce
n'etait pas une mise a jour qui ne s'applique pas, mais une mise a jour
appliquee a moitie.

- MapFallbackToFile n'heritait pas des StaticFileOptions : « / », le
  start_url de la PWA, repartait sans Cache-Control (mesure), donc en cache
  heuristique. A1 avait couvert tous les fichiers statiques sauf celui-la.
- register('service-worker.js') se resolvait contre l'URL du document et non
  contre <base> : une ouverture sur une route profonde visait
  /souhaits/service-worker.js, qui repond 404 (verifie). Tout le hors-ligne
  tombait alors en silence.
- clients.claim() et un rechargement force borne a une fois par session
  donnent un filet a la chaine SKIP_WAITING -> controllerchange -> reload.
- Une version prete dans les 10 s suivant l'ouverture s'applique seule, sans
  bandeau ; au-dela on repasse par le clic, pour ne pas arracher une saisie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:23:31 +02:00
mathieuandClaude Opus 5 347e1ce7fd Regroupe toute la documentation dans le README principal
PUBLICATION.md et A_FAIRE.md du dépôt du paquet sont fondus ici, avec le contenu
et les points de conception de mabibli_ynh/README.md. Une seule chaîne de
publication et un seul projet : deux jeux de documentation finissaient par
diverger, sans qu'on sache lequel faisait autorité.

Restent à part doc/DESCRIPTION.md et doc/ADMIN.md, que YunoHost lit lui-même
pour les afficher dans son catalogue et son interface d'administration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 19:49:30 +02:00
mathieu ce58294fd1 Ajout des bugs et idées dans la liste des choses à faire. 2026-08-19 23:39:16 +02:00
mathieuandClaude Opus 5 07cb48b2be Distinguer scénario, dessin et couleurs sur chaque livre
Le rôle vit sur le LIEN livre-auteur, jamais sur l'auteur : le même auteur
scénarise un album et dessine le suivant, et il ne doit pas exister en deux
fiches pour autant — ce que l'index unique sur la clé de regroupement
interdit de toute façon.

Les rôles se retrouvent par clé d'auteur résolue et non par position :
ResoudreAsync écarte les noms vides et fusionne les variantes, donc sa
liste peut être plus courte que la saisie.

Piège traité : le champ des noms reconstruit la liste à chaque frappe.
Decouper reprend les rôles connus en rapprochant sur la forme normalisée,
sans quoi corriger une faute de frappe les effacerait tous.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:37:20 +02:00
mathieuandClaude Opus 5 9ab6f23b8c Lire l'add-on EAN-2 et proposer le numéro de la revue scannée
Découverte à la mesure : l'EAN-2 est DÉSACTIVÉ par défaut dans zbar. Il ne
suffisait donc pas de cesser de le filtrer — il n'était jamais produit.
Vérifié sur un EAN-13 + EAN-2 rendus en canvas : un symbole par défaut,
deux après setConfig(ZBAR_EAN2, ZBAR_CFG_ENABLE, 1).

L'EAN-5 reste désactivé : il porte un prix, pas un numéro.

Ce qui reste non vérifié — que les magazines réels y mettent bien leur
numéro de parution — est dit à l'utilisateur : le numéro est proposé dans
un champ modifiable, sous une invitation à le comparer à la couverture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:30:21 +02:00
mathieuandClaude Opus 5 b97cf33494 Ramener les ligatures « œ » et « æ » à « oe » et « ae »
L'Œuvre de Zola, que la BnF écrit avec la ligature, était introuvable à
qui tape « oeuvre » : NFD sépare les accents mais laisse les ligatures.

Table explicite plutôt que NFKD, qui aurait aussi transformé les exposants,
les chiffres romains et les espaces insécables. Une normalisation de
recherche doit rester prévisible.

Sept colonnes normalisées en dépendent, dont cinq portent une unicité. Le
rattrapage au démarrage est donc étendu aux envies, séries, revues et
numéros — qui ne calculaient leurs formes qu'à l'écriture — et refuse toute
collision AVANT d'écrire : sinon l'exception tomberait au démarrage et le
serveur ne se lancerait plus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:25:59 +02:00
mathieuandClaude Opus 5 ca0a9dc18c Cataloguer les revues : une fiche, ses numéros à l'intérieur
Clôt la décision du premier lot, qui écartait les périodiques faute de
modèle. Douze numéros d'un magazine ne font plus douze fiches identiques.

Table à part, pour la troisième fois et pour le même motif que la liste
d'envies et les séries : logée dans Livres, une revue entrerait dans le
catalogue, ses compteurs, les doublons et les bibliographies, et il
faudrait écrire « et qui n'est pas une revue » à chaque lecture.

Un numéro est recensé, rien de plus — ni prêt ni statut, tous deux liés à
Livre par clé étrangère. Réversible.

« Créer ou retrouver » et non « créer » : scanner le numéro suivant du même
magazine retombe forcément sur la même revue. L'unicité de l'ISSN est un
index partiel, sans quoi les revues sans ISSN se bloqueraient entre elles.

Le flux 977 mène désormais à la fiche de la revue, et ne pré-remplit plus
le formulaire d'un livre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:19:08 +02:00
mathieuandClaude Opus 5 4e372ab694 Ajouter les sagas et cycles, avec leurs tomes manquants
Première relation entre livres du projet. Une série contient des séries
(un cycle EST une série de séries) et des PLACES dans l'ordre de lecture —
pas des livres : une place sans livre est le trou qu'on vient voir.

L'ordre est stocké, jamais déduit d'une année : une préquelle se lit avant
le livre paru dix ans plus tôt. Les tomes non possédés se saisissent à la
main, aucune source ne donnant l'ordre de lecture d'une saga.

Le titre de la place survit à la suppression du livre (SetNull, pas de
cascade), sans quoi perdre un exemplaire effacerait le tome 3 de la série.

Les séries sont communes au foyer. Seule exception : la mise en envies d'un
tome manquant, qui écrit dans une liste personnelle — c'est le seul point
où les deux portées se rencontrent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:03:08 +02:00
mathieuandClaude Opus 5 49eb8e1c98 Distinguer romans et BD par un type de document
Format disait physique ou numérique, jamais roman ou BD. Trois valeurs
seulement : une valeur s'ajoute sans migration, la renommer une fois posée
sur des centaines de fiches, non.

Le défaut est « non précisé », pas « roman » : les fiches existantes n'ont
pas été saisies avec cette question en tête, et un AddColumn suffit
justement parce que la valeur par défaut ne prétend rien. L'interface
n'affiche donc que ce qui a été choisi, comme pour le format.

Filtre ajouté aux DEUX implémentations, serveur et hors-ligne, avec quatre
jeux de critères de plus dans le test qui les confronte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:45:37 +02:00
mathieuandClaude Opus 5 a49125ddc3 Signaler les envies déjà entrées au catalogue, sans les supprimer
La demande initiale était de retirer l'envie ; c'est refusé. Le catalogue
est commun et la liste d'envies personnelle : supprimer modifierait la
liste d'un autre en silence, avec sa note. Et le rapprochement par clé
d'œuvre est faillible, alors qu'une suppression ne se rattrape pas.

L'étiquette « Déjà au catalogue » mène à la fiche, pour vérifier avant de
retirer. Le critère est celui des doublons, la convention d'auteur commun
étant désormais partagée par les deux (RapprochementAuteurs).

Au passage : accolade manquante sur .etiquette-souhaite, qui avalait la
règle suivante — le bandeau de mise à jour perdait son style.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:30:55 +02:00
mathieuandClaude Opus 5 383ed43227 Signaler les doublons à l'ajout d'un livre, sans jamais les refuser
Rien n'empêchait de rescanner un livre déjà catalogué. Plutôt qu'un index
unique — qui interdirait le second exemplaire, cas parfaitement légitime —
la création répond 409 avec les fiches semblables, et « confirmerDoublon »
enregistre la même saisie.

Deux critères, dont aucun n'est une clé : ISBN identique, ou clé d'œuvre
et auteur communs. La clé d'œuvre étant un préfixe de TitreNormalise, SQL
dégrossit sur la colonne indexée et l'égalité exacte se vérifie ensuite en
mémoire.

Vérifié en exécution : avertissement, retour au formulaire intact, et ajout
confirmé créant bien un second exemplaire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:11:39 +02:00
mathieuandClaude Opus 5 278d6579f7 Basculer le décodage des codes-barres de ZXing.Net vers zbar
L'ISBN 9782846391009 n'était pas décodé par l'application, alors qu'il
l'était par une application d'essai utilisant zbar, sur le même livre et
le même appareil.

⚠️ Le raisonnement qui avait écarté cette piste était faux : CLAUDE.md
concluait que « le décodeur n'y est pour rien » à partir de mesures de
VITESSE (4-6 ms/frame). La vitesse ne dit rien du taux de réussite.

Le décodage quitte donc le C# pour zbar compilé en WebAssembly, dont les
assets viennent du paquet ZBar.Blazor.

Ce que la bascule apporte, au-delà du code lu :
- plus aucun pixel ne traverse le pont JS→C# (un byte[] par frame avant),
  seule la valeur décodée le fait ;
- l'image ENTIÈRE est analysée, à 960 px, là où l'ancienne version
  recadrait sur la bande centrale à 640 px pour alléger ce transfert —
  c'était la seconde cause possible des codes non lus, elle disparaît ;
- zbar.wasm est chargé À LA DEMANDE, à la première ouverture du scanner.

⚠️ Le composant ZBarCamera du paquet n'est PAS utilisé : il ouvre la
caméra lui-même et avale les erreurs. On y perdrait les messages qui
distinguent permission refusée, absence de caméra, caméra occupée et
contexte non sécurisé, ainsi que facingMode environment (caméra arrière)
et l'indication de résolution. Seuls zbar.js et zbar.wasm sont empruntés.

Poids, comparaison de deux publish Release complets (somme brotli) :
  _framework  3 281 558 → 2 978 700 o   (−296 Kio au démarrage)
  total       3 281 558 → 3 125 541 o   (−152 Kio)
L'écart vient de la traîne que ZXing imposait au trimmer :
System.Text.RegularExpressions retombe de 98 374 à 7 137 o, et
System.Runtime.Numerics disparaît.

⚠️ Coût assumé : six tests de décodage disparaissent avec IsbnScanner,
le décodeur n'étant plus en C#. Le décodage a été vérifié dans le
navigateur sur un EAN-13 rendu en canvas — 9782846391009 ressort bien en
ZBAR_EAN13 — mais cela reste une vérification, pas un garde-fou.
À confirmer avec le livre en main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:51:56 +02:00
mathieuandClaude Opus 5 718df2c986 Consigner les retours d'usage du 2026-08-19 (3ᵉ série)
Trois points remontés, tous avec leur arbitrage identifié mais non tranché :

- doublon à l'ajout : vérifié, CreerAsync ne contrôle rien et l'index sur
  Isbn n'est pas unique. Reste à décider refus ou avertissement, un second
  exemplaire étant légitime ;
- retirer l'envie correspondante à l'ajout : la difficulté est la portée
  (catalogue commun, envies personnelles), pas le rapprochement ;
- BD et magazines : les BD demandent surtout un type de document, les
  magazines rouvrent la décision actée de ne pas cataloguer les périodiques.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:35:17 +02:00
mathieuandClaude Opus 5 281d74a29d Documenter le choix de la bibliothèque de code-barres, à trancher
L'ISBN 9782846391009 n'est pas décodé par l'application, mais l'est par
une application d'essai avec ZBar, sur le même appareil et le même livre.

Le raisonnement qui écartait ce sujet était faux : CLAUDE.md conclut que
« le décodeur n'y est pour rien » à partir de mesures de VITESSE, qui ne
disent rien du taux de réussite.

Poids mesuré sur le publish Release de l'application d'essai : ZBar coûte
~168 Ko brotli, contre ~346 Ko déjà documentés pour ZXing.Net — dont la
queue d'assemblies BCL (Regex à 98 Ko, Numerics) que ZBar ne traîne pas.
Le remplacement allégerait l'application d'environ 180 Ko, à confirmer
par une comparaison de deux publish du projet.

Rien n'est acté : la décision se paie du départ du décodage hors du C#,
qui était la justification du choix de ZXing.Net.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:33:47 +02:00
mathieuandClaude Opus 5 455c2b711a Retirer d'IDEES.md une entrée déjà traitée
« Titres BnF contenant le nom de l'auteur » y figurait encore comme
correction envisagée, alors que NettoyageIsbd.RetirerAuteurEnFin la met
en œuvre depuis la phase du lookup ISBN — avec des tests sur le cas réel
qui l'avait motivée (ISBN 9782749942636, « … Aurélien Barrau » recollé
en fin de titre sans séparateur ISBD).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:15:09 +02:00
mathieuandClaude Opus 5 5cf566bc33 Traiter les retours d'usage du 2026-08-18 (2ᵉ série)
Six lots, issus d'IDEES.md. Les décisions actées sont reportées dans
CLAUDE.md, et les entrées traitées retirées d'IDEES.md.

1. Douchette USB, ISSN et enchaînement du scan
   - Champ ISBN focalisé à l'ouverture : c'est tout ce qu'exige une
     douchette USB, qui se présente au système comme un clavier. Remède
     au scan caméra, qui rate sur la webcam d'un PC (optique, pas
     décodage).
   - Un EAN en 977 est un périodique : il porte un ISSN, donc un titre de
     revue. Intercepté AVANT la validation ISBN (c'est un EAN-13
     parfaitement valide), l'ISSN est déduit du code puis nommé via
     bib.issn. Auparavant la cascade s'exécutait en entier pour finir sur
     « aucun résultat ».
   - L'enchaînement après scan existait déjà mais était invisible : une
     étape « Recherche » affiche désormais le code interrogé.

2. ISBN affichés avec des tirets
   Tables extraites du RangeMessage.xml officiel — plusieurs tranches ne
   sont pas celles qu'on suppose. Le francophone est découpé en entier,
   ailleurs on s'arrête au groupe et à la clé : aucune coupure fausse.
   Corrige au passage l'export CSV, où un ISBN nu était lu comme un
   nombre par Excel.

3. Hors-ligne : la liste d'envies
   ListerSouhaitsAsync était le seul point de lecture hors du dispositif
   hors-ligne, d'où le « 404 Not Found » brut à l'écran. Cinquième
   instantané, écritures refusées, plus aucun message HTTP. Même défaut
   corrigé sur la bibliographie.

4. Navigation par onglets
   Catalogue / Auteurs / Prêts / Envies dans MainLayout ; les barres
   d'actions ne portent plus que des actions. Filtres repliés derrière un
   bouton compteur, ligne « format » masquée quand le fonds n'a qu'un
   format. Une seule entrée d'ajout, désactivée hors-ligne — pas masquée.

5. Liste d'envies : ordre, recherche, couvertures
   Migration RangDesEnvies. Le remplissage reconduit l'ordre affiché
   jusqu'ici : sans lui, les listes existantes se seraient réordonnées
   toutes seules. Réordonnancement par flèches et glisser-déposer (le
   drag & drop HTML5 ne marche pas au doigt). Ajout dans son propre
   écran, avec recherche par titre (bib.title) et couvertures enfin
   alimentées.

6. Bibliographie : une source muette n'est pas une liste vide
   L'écran affichait « BnF injoignable » PUIS « la BnF ne connaît aucun
   livre de cet auteur » — la seconde phrase étant fausse. Les deux cas
   s'excluent désormais, et un bouton Réessayer est offert.

   Vérifié en exécution : le diagnostic d'IDEES.md était faux sur un
   point. Robert A. Harper a bien 7 œuvres à la BnF (85 notices
   annoncées) ; c'était le même délai dépassé observé deux fois, pris
   pour deux causes distinctes.

380 tests, dont un qui applique réellement la migration (EnsureCreated
n'en joue aucune) et un qui verrouille les messages atteignant
l'utilisateur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:14:01 +02:00
mathieu 2f1e854ea3 Ajout fichier config de rider 2026-08-18 21:09:32 +02:00
mathieuandClaude Opus 5 bfbd90fabd Documenter l'installation YunoHost éprouvée en production
Section « Installation YunoHost » : la chaîne réelle, les pièges de
publication (dépôt privé qui répond 404, manifeste lu depuis Gitea), les
deux pièges systemd avec leur mesure, l'écart entre le propriétaire
d'install_dir déclaré et celui appliqué, et ce qui a été vérifié en
exécution — installation, WAL, sauvegarde, mise à jour, désinstallation.

L'étape 9 des prochaines étapes est faite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:05:42 +02:00
mathieuandClaude Opus 5 922fd024e7 Ajouter la licence AGPL-3.0-or-later
Licence confirmee par l'utilisateur. Le depot n'avait aucun fichier
LICENSE, alors que le paquet YunoHost declarait deja cette licence dans son
manifeste — les deux sont desormais coherents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:06:09 +02:00
mathieu 580d8469ac Fusionner le cache hors-ligne IndexedDB
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

# Conflicts:
#	MaBibli.Client/wwwroot/css/app.css
2026-08-18 14:06:07 +02:00
mathieuandClaude Opus 5 b89b8ac964 Sortir du mode hors-ligne quand la panne venait du serveur, et documenter
Constate en essai : quand c'est le serveur qui tombe, navigator.onLine n'a
jamais change, donc aucun evenement « online » ne viendra jamais. Les lectures
court-circuitaient sur EnLigne et ne retentaient plus rien : l'application
restait sur le cache jusqu'au rechargement de la page.

EtatReseau distingue donc deux notions : EnLigne (ce que l'interface affiche et
ce qui active les boutons) et TenterLeReseau (faut-il tenter un appel, vrai des
que le navigateur a une connexion, meme apres un echec). Verifie : serveur
arrete puis redemarre sans aucun evenement « online », la navigation suivante
retire le bandeau, reactive les actions et fait apparaitre un livre ajoute
cote serveur pendant la coupure.

CLAUDE.md : architecture retenue, ce qui a ete observe reseau coupe, pourquoi
le filtre existe en deux exemplaires, ce que le hors-ligne ne couvre pas, la
chaine de mise a jour du service worker, et la cause etablie de son echec
d'enregistrement dans le navigateur d'automatisation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:04:33 +02:00
mathieuandClaude Opus 5 53dd8cb6bd Fusionner la liste d'envies personnelle et son export
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:04:27 +02:00
mathieuandClaude Opus 5 231b0b2a11 Acter la liste d'envies dans CLAUDE.md et nettoyer IDEES.md
Retire d'IDEES.md la section « Pour plus tard — livres souhaités », désormais
implémentée, et acte dans CLAUDE.md les trois décisions structurantes : table
dédiée pour la liste d'envies, SRU BnF par bib.author pour la bibliographie,
export en .txt et .csv.

Consigne dans CLAUDE.md ce que la source renvoie réellement (Werber 203 notices
-> 51 œuvres, Zola 2692 annoncées -> 87 œuvres sur 200 lues) et ce que le
rapprochement par titre rate, l'interface énonçant la même limite à l'écran.

Deux points relevés en passant et laissés à IDEES.md : la ligature « œ » que
NormalisationTexte ne réduit pas à « oe » (L'Œuvre de Zola y échappe, et le
défaut touche aussi la recherche du catalogue), et le plafond de 200 notices.

Corrige au passage une ponctuation bancale du bandeau de bibliographie, et
restreint le BOM UTF-8 au seul CSV.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:03:29 +02:00
mathieuandClaude Opus 5 ebc5f95d49 Consulter la bibliotheque hors-ligne depuis un cache IndexedDB
Le piege que CLAUDE.md signale : en Blazor WebAssembly le code tourne dans le
navigateur alors que SQLite vit sur le serveur, et le service worker ne met en
cache que les assets. Sans travail explicite, l'application demarre hors-ligne
et affiche une bibliotheque vide.

- js/cache-hors-ligne.js : instantanes JSON dans IndexedDB, et surveillance des
  bascules online/offline. Aucune logique metier.
- CacheHorsLigne / EtatReseau : lecture-ecriture des instantanes, et etat reseau
  combinant navigator.onLine (fiable seulement par sa negation) avec le sort
  reel des appels HTTP.
- ServiceLivresApi : les lectures retombent sur le cache, les ecritures sont
  refusees. Le catalogue entier est memorise, pas les reponses filtrees : c'est
  ce qui rend la recherche hors-ligne possible sur tout le fonds.
- FiltreLivresLocal : pendant navigateur de FiltreLivres, avec un test qui
  confronte les deux implementations sur les memes donnees.
- Interface : pastille et bandeau d'etat avec la date de synchronisation, et
  actions d'ecriture desactivees avec leur raison plutot que boutons morts.
- js/mise-a-jour.js : les empreintes WASM etant desactivees, le service worker
  est le seul cache-busting du projet. L'enregistrement journalise desormais ses
  echecs, et un bandeau propose la nouvelle version sans attendre la fermeture
  de tous les onglets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:59:19 +02:00
mathieuandClaude Opus 5 8b8fe3be6e Ajouter la liste d'envies personnelle, son export et la bibliographie par auteur
La liste d'envies vit dans une table séparée (LivreSouhaite) plutôt que dans un
statut de plus sur Livre : un livre souhaité n'est pas possédé, et le loger dans
Livres l'aurait fait entrer dans le catalogue, les compteurs et les prêts, au
prix d'un « et qui n'est pas souhaité » à répéter dans chaque lecture. La portée
est personnelle, comme le statut de lecture — mais ici Utilisateur est une vraie
frontière : toute lecture filtre dessus.

Bibliographie : SRU BnF interrogé par bib.author, vérifié le 2026-08-18. Deux
filtres mesurés sur des réponses réelles sont indispensables — le type de
document (l'index mêle livres audio, jeux et spectacles) et surtout l'auteur
réel de la notice, « all » rapprochant les mots sur l'ensemble des auteurs :
« Émile Zola » remonte sinon toute l'œuvre de sa fille Denise Le Blond-Zola.
Les rééditions sont regroupées par clé d'œuvre (183 notices Werber -> 51
œuvres). Le rapprochement avec l'étagère se fait par titre, pas par ISBN, qui
désigne une édition et non une œuvre ; ses limites sont dites à l'écran.

Export en deux formats, tous deux du texte sans dépendance : .txt groupé par
auteur pour la librairie, .csv à séparateur point-virgule et BOM pour le
tableur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:59:06 +02:00
mathieuandClaude Opus 5 3a5a3a7763 Fusionner les retours d'interface (fiche en lecture seule, couverture agrandissable, statut et format)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 07:14:41 +02:00
mathieuandClaude Opus 5 08e57fbbc4 Acter les choix d'interface et retirer les points traites d'IDEES.md
CLAUDE.md gagne une section « Interface — décisions actées » : les deux modes
de la fiche et le mode porté par l'URL, les deux exceptions assumées qui
restent hors édition (statut, prêts), la liste en lecture seule, le format
réservé aux ebooks et son corollaire sur le formulaire, l'agrandissement de
couverture. Y figure aussi le piège Blazor du routeur qui ne redessine pas
quand aucun paramètre ne change.

IDEES.md perd sa section « Interface — retours d'usage », désormais
entièrement traitée. La recherche sans accents y reste, mais annotée : elle
paraît réglée par la refonte du schéma — « Emile » remonte bien « Émile Zola »
sur l'API — sans avoir été traitée comme un point à part entière.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 07:13:46 +02:00
mathieuandClaude Opus 5 6b783bafa0 Appliquer les retours d'interface : fiche en lecture seule, liste allegee
Cinq retours d'IDEES.md, tous pensés pour le mobile d'abord.

Fiche du livre en deux modes, portés par deux routes : « /livres/{id} »
consulte, « /livres/{id}/edition » modifie. Ouvrir une fiche depuis le
catalogue ne place plus un formulaire vivant sous le pouce. Mettre le mode
dans l'adresse fait du retour navigateur une sortie d'édition naturelle.

Piège rencontré : les deux routes visent le même composant avec le même Id,
donc aucun paramètre ne change et le routeur ne redessine pas — l'URL passait
à « /edition » sans que l'écran bascule, alors qu'un chargement direct de
cette URL marchait, ce qui masque le défaut. Le composant s'abonne donc à
LocationChanged.

Restent hors « Éditer », parce que ce ne sont pas des modifications de la
fiche : le statut de lecture (action la plus fréquente, et personnelle) et
les prêts (gestes du quotidien, donnée du foyer). La suppression, elle, est
réservée au mode édition.

Dans la liste, le statut devient une pastille en lecture seule sur la ligne
des étiquettes ; les trois boutons actionnables par carte partaient trop
facilement en faisant défiler. Le format ne s'affiche plus que pour les
ebooks, le physique étant le cas par défaut — le formulaire le
présélectionne déjà, l'énumération valant 0.

Couverture agrandissable au clic, fermeture au clic extérieur ou par Échap
(le calque est focalisé à l'ouverture, sans quoi Échap resterait sans effet).
Le substitut à initiale n'est jamais cliquable : le déclencheur n'existe que
s'il y a une image.

Le regroupement par auteur existait déjà (table Auteur, page /auteurs, filtre
« ?auteur= ») ; y manquaient les liens depuis la fiche et les styles de la
page auteurs, jamais écrits — la liste sortait en puces brutes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 07:13:37 +02:00
mathieuandClaude Opus 5 60de1d8784 Fusionner la gestion des prets
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:45:50 +02:00
mathieuandClaude Opus 5 2a3dfb88de Gerer les prets : API, ecrans et contrainte en base
L'entite Pret existait depuis le squelette sans jamais etre exploitee. Cette
phase la met en service de bout en bout.

API
- GET  /api/prets/en-cours       ce qui n'est pas a la maison, du plus ancien
                                 au plus recent : on cherche le livre oublie,
                                 pas celui prete hier
- POST /api/livres/{id}/prets    preter
- GET  /api/livres/{id}/prets    historique complet, du plus recent au plus
                                 ancien
- POST /api/prets/{id}/retour    clore le pret sans le supprimer

Un livre deja sorti ne peut pas etre prete une seconde fois. Le service le
verifie et nomme celui qui l'a deja, mais entre sa verification et l'insertion
il reste une fenetre : un index unique PARTIEL (LivreId WHERE DateRetour IS
NULL) la ferme, tout en laissant l'historique accumuler autant de prets clos
que necessaire sur le meme livre.

Les ebooks sont refuses cote API, pas seulement grises dans l'interface : une
fiche n'a pas d'exemplaire a confier.

Les prets sont COMMUNS au foyer, symetrique inverse du statut de lecture. Le
service ne recoit meme pas d'identite, pour qu'on ne puisse pas s'en servir
par inadvertance.

Interface, pensee mobile d'abord
- ecran « Prets en cours » avec bouton « Rendu » a meme la liste
- bloc pret sur la fiche d'un livre : etat courant, action, puis historique
- etiquette « Prete a X » dans le catalogue
- la barre d'actions passe sur deux lignes plutot que de comprimer ses
  libelles maintenant qu'elle compte quatre entrees

Toutes les dates sont en UTC ; le client convertit la date locale du
<input type="date"> avant l'envoi, faute de quoi le pret se decalerait d'un
jour pour la moitie du globe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:45:01 +02:00
mathieuandClaude Opus 5 94a186f4a7 Fusionner la reparation du publish et le correctif NettoyageIsbd
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:44:07 +02:00
mathieuandClaude Opus 5 0fde244d0b Couper le role BnF meme quand le prenom finit par une initiale
NettoyageIsbd.Auteur("Cormen, Thomas H. Auteur du texte") renvoyait
« Thomas H. Auteur du texte Cormen » : le point de l initiale etait
protege, donc plus aucun point ne servait de separateur de role.

Ce qui tranche : dans « Nom, Prenom », la seule chose qui puisse suivre
une initiale et faire encore partie du nom est une autre initiale
(« Tolkien, J. R. R. »). Tout le reste est le role. Le point est conserve
quand il clot une initiale, pour ne pas amputer le prenom.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:43:07 +02:00
mathieuandClaude Opus 5 9cb3ca535d Reparer la publication de l API (index.html cassé)
Le publish de MaBibli.Api recopiait index.html tel quel, placeholders
compris, et l application publiee restait blanche.

Cause : dans le SDK .NET 10, la cible de publication
GenerateHtmlAssetPlaceholdersPublishStaticWebAssets lit la liste des
fichiers HTML dans @(_HtmlStaticWebAssets), item alimente uniquement par
la cible de *build*. Quand c est l API qui demande au client ses assets de
publication, la cible de build n a pas tourne : la liste est vide, aucun
asset HTML calcule n entre dans le manifeste, et c est le fichier source
qui est recopie par ComputeResolvedFilesToPublishList.

Correction : supprimer le besoin de reecriture plutot que la reparer.
WasmFingerprintAssets=false et pas de OverrideHtmlAssetPlaceholders (c est
lui qui, a true, forcait l empreinte de blazor.webassembly.js) ; index.html
reference des noms stables, sans import map ni preload placeholder. Le
contournement « ?m=1 » de l import du module scanner devient inutile.

Verifie : publish self-contained lance, scripts references en 200,
application chargee dans un navigateur (catalogue affiche, module JS du
scanner charge, aucun 404).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:42:58 +02:00
mathieuandClaude Opus 5 ab878b4d3c Ignorer .claude/ (worktrees d agents)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:25:12 +02:00
mathieuandClaude Opus 5 d9e6cf425d Fusionner la phase base de donnees (statut par utilisateur, table Auteur)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:24:09 +02:00
mathieuandClaude Opus 5 49d1528988 Fusionner la phase scan camera (ZXing.Net)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:24:01 +02:00
mathieuandClaude Opus 5 30a5cc12cf Documenter le nouveau schema dans CLAUDE.md
Le bloc « Modele de donnees » decrivait encore Auteur et Statut comme des
colonnes de Livre. Il reflete desormais les cinq tables, leurs index et
leurs contraintes d'unicite.

Ajoute aussi ce qui ne se devine pas a la lecture du schema : pourquoi
des colonnes normalisees existent (SQLite ne sait pas ignorer les
accents), pourquoi Auteur en porte deux, et ce que la migration fait des
donnees existantes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:22:54 +02:00
mathieuandClaude Opus 5 b594a4b2c1 Sortir le statut de lecture et l'auteur de la table Livre
Deux changements de modele en une seule migration, plus la recherche qui
en depend.

Le statut de lecture devient personnel. Il etait une colonne de Livre,
donc partage par tout le foyer, alors que deux membres lisent le meme
exemplaire a des rythmes differents. Il vit desormais dans une table
(LivreId, Utilisateur, Statut) avec unicite sur le couple. L'absence de
ligne vaut « non commence » : sur une bibliotheque de foyer la plupart
des couples n'ont aucun statut, et les materialiser tous multiplierait
les lignes par le nombre de comptes pour n'exprimer qu'un vide. Rien
n'est donc ecrit a la creation d'un livre.

Piege rencontre : un Dictionary<int, Statut> renvoyait la valeur 0 de
l'enumeration — « À lire » — pour un livre sans ligne, rendant « non
commence » indiscernable d'un choix explicite. Le dictionnaire est
desormais typé Statut?.

L'auteur devient une table. Deux formes normalisees y cohabitent, et ce
n'est pas une redondance : NomNormalise garde l'ordre de saisie pour la
recherche en sous-chaine, CleRegroupement trie les mots et porte l'index
unique, donc l'invariant « un auteur, une fiche ». Les initiales
echappent a la cle et sont traitees en memoire, sur une table qui compte
au plus quelques centaines de lignes.

Un livre peut avoir plusieurs auteurs — le lookup ISBN en renvoie quatre
pour Introduction to Algorithms — d'ou la table de liaison, avec une
position qui conserve l'ordre de la couverture.

Les rapprochements ambigus ne sont jamais appliques seuls : l'API les
liste, l'utilisateur accepte ou refuse, et les refus sont memorises pour
que la suggestion ne revienne pas. Le couple refuse est range par
identifiant croissant, donc un refus vaut dans les deux sens.

Reprise des donnees existantes. L'ancien statut, commun, est rattache a
AjoutePar — seule personne que la base associe au livre. Les statuts des
livres sans AjoutePar sont perdus : les attribuer serait une invention.
Les trois valeurs sont reprises telles quelles, « À lire » compris, parce
que c'est ce que l'ancienne interface affichait. L'ancien champ auteur
devient une fiche par valeur distincte.

La migration ne peut pas tout faire : lower() de SQLite ne retire pas les
accents, donc « Émile Zola » et « emile zola » y restent deux fiches.
ServiceRenormalisation finit le travail en C# au demarrage, reunit ces
variantes, applique aussi la regle des initiales — sans quoi une base
heritee resterait eclatee la ou une saisie neuve aurait ete reunie
d'emblee — et garde le nom d'affichage le plus presentable. Il est
idempotent, et sert de filet si les regles de normalisation changent.

L'ordre de la migration compte : les colonnes condamnees sont recopiees
dans une table de transit avant d'etre supprimees, parce que supprimer
une colonne sous SQLite reconstruit la table.

Verifie sur une base a l'ancien schema contenant 9 livres, 2 prets et
trois variantes de Zola : prets intacts, statuts rattaches, les trois
Zola reunis sous « Émile Zola », « P.F. Hamilton » absorbe par
« Peter F. Hamilton », « Hamilton » seul laisse en suggestion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:22:46 +02:00
mathieuandClaude Opus 5 4a8bc98f50 Retirer le nom de l'auteur colle en fin de titre BnF
Cas mesure, ISBN 9782749942636 :

  dc:title   = "Le plus grand defi ... (Nouvelle ed. augmentee) Aurélien Barrau"
  dc:creator = "Barrau, Aurélien (1973-....). Auteur du texte"

Le nom est recolle en fin de titre sans le separateur ISBD « / », donc la
regle de nettoyage existante ne peut rien y faire : la notice est sale a
la source.

L'heuristique est volontairement stricte — comparaison en fin de chaine
uniquement, contre un auteur deja nettoye, et seulement si le nom
commence sur une frontiere de mot. « Lettre a Émile Zola » n'est pas
touche, et un titre qui EST le nom de l'auteur (biographie, recueil) est
laisse intact plutot que reduit a rien.

La comparaison passe par CompareOptions.IgnoreNonSpace plutot que par une
normalisation NFD : elle ignore les accents sans changer les longueurs,
donc les indices calcules restent valables sur la chaine d'origine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:22:24 +02:00
mathieuandClaude Opus 5 493cf80615 Normaliser les textes et poser les regles de rapprochement d'auteurs
SQLite ne sait pas comparer sans tenir compte des accents : sa collation
NOCASE ne couvre que l'ASCII et il n'existe pas de unaccent() integre. La
seule facon d'obtenir « Emile » vers « Émile » sans charger tout le
catalogue en memoire est de stocker la forme mise a plat, et d'y chercher
un terme passe par exactement la meme fonction.

NormalisationTexte fait cette mise a plat. La ponctuation y devient une
espace au lieu de disparaitre : « P.F. Hamilton » doit donner trois mots,
sans quoi le rapprochement par initiales serait impossible.

RapprochementAuteurs porte la frontiere decidee dans CLAUDE.md. La cle de
regroupement — mots normalises tries — couvre casse, accents et ordre
inverse, et servira d'index unique. Les initiales abregees lui echappent :
elles demandent d'aligner les mots un a un, avec retour arriere, parce
qu'une correspondance gloutonne echouerait sur « P. Peter » face a
« Peter Paul ».

Le verdict est a trois niveaux, et l'asymetrie est deliberee : ne pas
fusionner laisse deux fiches a reunir d'un clic, alors qu'une fusion
erronee melange les livres de deux personnes et se defait mal. Un
patronyme seul face a un nom complet ressort donc « suggere », jamais
« certain ». Meme exigence sur l'alignement complet : « P. H. » et
« Peter Hamilton » s'alignent par les seules initiales, ce qui ne
concorde sur rien de verifiable — un mot entier commun est exige.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:22:14 +02:00
mathieuandClaude Opus 5 71d4eb6752 Documenter un bloquant : le publish de l'API casse index.html
Verifie en executant le binaire publie : la racine repond 200 mais les
scripts de demarrage renvoient 404, donc l'application publiee affiche une
page blanche. Les placeholders d'empreinte restent litteraux alors que
seuls les fichiers empreintes existent sur disque.

Preexistant depuis la phase 1 : ma verification d'alors se limitait au code
HTTP de la racine, ce qui ne prouve pas que l'application demarre.

Consigne les trois pistes deja essayees sans succes pour eviter qu'on les
retente, et signale que le developpement n'est pas affecte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:16:29 +02:00
mathieuandClaude Opus 5 3a00591117 Corriger deux blocages du scan trouves en navigateur, et consigner les mesures reelles
Les deux defauts ont ete constates en executant reellement l'application, pas par
relecture :

1. L'import du module JS partait en 404 en developpement. L'import map generee par
   Blazor reecrit « ./js/scanner-camera.js » vers un nom empreinte que l'API hote ne
   sert pas (UseStaticFiles ignore les points d'entree empreintes). Une chaine de
   requete sur le specifieur empeche la reecriture ; en publication rien n'est
   reecrit pour ce fichier, le comportement est donc identique dans les deux modes.

2. Les messages d'erreur camera etaient tous generiques. Une DOMException perd son
   « name » en traversant le pont JS vers C# : un refus de permission arrivait sous
   la forme « Permission denied undefined ». Le JS attrape donc l'erreur et renvoie
   un code de statut stable, que le C# traduit en message utile.

CLAUDE.md est mis a jour avec les mesures reelles obtenues en WASM, qui manquaient :
la performance de decodage (l'estimation de 5-10 ms/frame est confirmee, l'AOT reste
inutile) et le surcout de payload, nettement superieur aux 192 Ko documentes une fois
comptees les assemblies BCL que ZXing empeche le trimmer de retirer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:10:58 +02:00
mathieuandClaude Opus 5 f7ad9582f6 Ajouter le scan camera du code-barres ISBN
Le decodage EAN-13/EAN-8 est fait en C# par ZXing.Net (IsbnScanner), conformement
a la decision actee dans CLAUDE.md : le JavaScript ne fournit que les pixels de la
camera, aucune logique de decodage ne lui est confiee.

Le scan se greffe en amont du flux d'ajout par ISBN existant : des qu'un code est
lu et reconnu comme ISBN-13 valide, la recherche de notices est declenchee sans
que l'utilisateur ait a retaper quoi que ce soit. La saisie manuelle reste
accessible en permanence, y compris quand la camera echoue.

Points traites explicitement :
- camera arriere par defaut (facingMode "environment", en "ideal" pour ne pas
  echouer sur un PC sans camera arriere) ;
- liberation de la camera a l'annulation, au succes et au demontage du composant ;
- cadence limitee a ~12 images/s et decodage restreint a la bande centrale
  reduite a 640 px de large : inutile de decoder chaque frame en pleine taille ;
- messages distincts pour permission refusee, absence de camera, camera occupee
  et contexte non securise (getUserMedia exige HTTPS ou localhost : une IP de
  reseau local en http ne marchera jamais, c'est une source de confusion garantie).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:32:05 +02:00
mathieuandClaude Opus 5 b619de29c7 Acter le statut de lecture par utilisateur et la table Auteur
Le statut de lecture sort de `Livre` vers une table par utilisateur : le
livre reste commun au foyer, sa lecture devient personnelle. La decision
« collection commune » n'est pas remise en cause.

L'auteur passe d'un champ texte libre a une table dediee avec forme
normalisee, prerequis du regroupement par auteur et de la recherche
insensible aux accents. Le regroupement automatique est limite aux
variantes sures ; les rapprochements ambigus doivent etre proposes, pas
appliques silencieusement — une fusion erronee se defait mal.

IDEES.md : la liste d'envies sera personnelle elle aussi, et exportable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:21:37 +02:00
mathieuandClaude Opus 5 a314dcfc79 Recueillir les idees d'interface dans IDEES.md
Retours d'usage a traiter plus tard : agrandissement de la couverture au
clic, statut de lecture en lecture seule dans la liste et remonte au
niveau du type, affichage du format limite aux ebooks (physique par
defaut), fiche en lecture seule avec bouton d'edition, regroupement des
livres par auteur.

Documente aussi un cas de donnee sale mesure sur l'ISBN 9782749942636 :
la notice BnF colle le nom de l'auteur en fin de titre sans separateur
ISBD, donc la regle de nettoyage existante ne peut pas s'appliquer. Ce
n'est ni un doublon ni un defaut du parsing.

Garde en fin de fichier l'idee des livres souhaites (bibliographie par
auteur avec ouvrages possedes grises), hors perimetre v1, avec ses
implications sur le modele de donnees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:16:16 +02:00
mathieuandClaude Opus 5 fd6438b912 Phase 3 : CRUD des livres, statuts de lecture et identite SSOwat
API
- CRUD complet sur /api/livres : liste filtrable (format, statut) et
  cherchable (titre, auteur), consultation, creation, modification,
  changement de statut isole, suppression.
- Aucune lecture ne filtre sur AjoutePar : la bibliotheque est commune au
  foyer. Le critere n'existe meme pas dans CritereLivres, pour qu'on ne
  puisse pas s'en servir par inadvertance. Deux tests verrouillent
  l'invariant, dont un par reflexion sur le type des criteres.
- AjoutePar est renseigne par le serveur a la creation, jamais par le
  client, et n'est pas reecrit a l'edition (pas plus que DateAjout).
- Identite lue dans les en-tetes SSOwat (YNH_USER, YNH_USER_EMAIL,
  YNH_USER_FULLNAME), avec repli sur un utilisateur simule configure en
  developpement uniquement. Sans en-tete ni simulation, l'identite reste
  inconnue plutot qu'inventee. Expose par GET /api/moi.
- Migration appliquee au demarrage : pas d'etape manuelle a l'installation.

Interface Blazor, pensee mobile d'abord
- Catalogue unique (physiques et numeriques melanges, distingues par le
  format) avec recherche, filtres et changement de statut directement dans
  la liste.
- Ajout par ISBN : quand la cascade rend plusieurs notices, un ecran de
  choix les presente en mettant en avant editeur et annee, seuls elements
  qui les departagent. Le formulaire reste ensuite entierement modifiable.
- Ajout manuel, edition, suppression avec confirmation explicite.
- Couvertures : l'URL OpenLibrary n'est jamais validee par un appel
  prealable (502 intermittents mesures en phase 2) ; un attribut onerror
  masque l'image cassee et laisse apparaitre un substitut.

Decisions prises la ou CLAUDE.md etait muet
- Tri alphabetique par titre.
- Un ISBN saisi doit etre valide, mais reste facultatif.
- Ecran de choix affiche seulement a partir de deux candidats.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:55:07 +02:00
mathieuandClaude Opus 5 aed03e6b6f Documenter deux constats operationnels issus de la phase 2
- Couvertures OpenLibrary : 502 intermittents mesures sur un meme ISBN.
  Ne pas valider l'URL par un HEAD ; c'est a l'interface de gerer l'image
  cassee.
- OpenLibrary : certaines editions n'ont aucun champ `authors`, le double
  appel documente ne suffit donc pas. Le repli via `/works/` est desormais
  implemente et documente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:26:48 +02:00
mathieuandClaude Opus 5 f8f30dbcf7 Phase 2 : service de lookup ISBN en cascade BnF puis OpenLibrary
Recuperation des metadonnees d'un livre a partir de son ISBN, cote serveur
uniquement (aucune interface, aucune persistance).

Cascade : BnF en ISBN-13, puis BnF en ISBN-10 converti, puis OpenLibrary.
La conversion 13 -> 10 est indispensable et non optionnelle : la BnF indexe
l'ISBN tel qu'imprime, les ouvrages d'avant 2007 ne portent qu'un ISBN-10 et
sont introuvables par l'EAN-13 que lit le scanner.

Toutes les notices trouvees sont remontees (jusqu'a 5) : un meme ISBN peut
correspondre a plusieurs reeditions, le choix revient a l'utilisateur.

Le double appel /isbn puis /authors d'OpenLibrary est bien fait, avec repli
sur l'oeuvre quand l'edition ne porte aucun auteur : c'est le bug de BookLogr
(titre rempli, auteur vide) qu'il ne faut pas reproduire.

La couverture vient toujours d'OpenLibrary, avec ?default=false pour obtenir
un 404 plutot qu'une image placeholder.

87 tests xUnit, sur fixtures enregistrees : aucune dependance au reseau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:22:47 +02:00
mathieuandClaude Opus 5 4990e41f3e Phase 1 : squelette de la solution (3 projets .NET 10)
Mise en place de la structure de base, sans aucune fonctionnalité métier :

- MaBibli.sln à la racine, avec trois projets ciblant net10.0
- MaBibli.Client : Blazor WebAssembly PWA (dotnet new blazorwasm --pwa),
  pages d'exemple du modèle (Counter, Weather) retirées
- MaBibli.Api : ASP.NET Core, sert aussi les fichiers statiques du client
  (UseBlazorFrameworkFiles + MapFallbackToFile), via le package
  Microsoft.AspNetCore.Components.WebAssembly.Server
- MaBibli.Shared : entités et enums partagés (Livre, Pret, Format, Statut),
  conformes au modèle de données de CLAUDE.md — AjoutePar est une simple
  traçabilité, Pret est une table séparée pour garder l'historique complet
- EF Core + SQLite dans l'API : MaBibliDbContext, chaîne de connexion,
  et migration initiale InitialCreate vérifiée par un database update
- dotnet-tools.json : dotnet-ef épinglé en outil local

Vérifié : dotnet build sans avertissement, et
dotnet publish MaBibli.Api -c Release -r linux-x64 --self-contained
produit un dossier unique dont le wwwroot contient bien _framework/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:52:37 +02:00
mathieuandClaude Opus 5 4467456b8b Cloturer le cadrage : deploiement, build, cache et notices BnF
- Architecture serveur : x86_64, publish `linux-x64`.
- Production des binaires : compilation locale + release manuelle, via un
  script ecrit pour etre reutilisable en CI plus tard. Compiler sur le
  serveur est explicitement exclu.
- Cache hors-ligne : IndexedDB, retenu pour permettre recherche et tri
  hors-ligne sur toute la bibliotheque.
- Notices BnF multiples : demander systematiquement, l'exactitude de
  l'edition primant sur la vitesse de saisie.
- AOT WASM : desactive par defaut, a reevaluer apres mesure sur telephone.

Ajoute une section sur l'architecture de deploiement : les 3 projets .NET
ne produisent qu'un seul service (le client WASM est servi par l'API), et
le packaging vit dans un depot `mabibli_ynh` separe du code.

Le cadrage est desormais clos.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:45:20 +02:00
mathieuandClaude Opus 5 a30197000f Valider l'API SRU de la BnF et documenter ses pieges
API testee : HTTP 200, ~240 ms, sans cle. Le schema `dublincore` evite
completement l'UNIMARC — mapping bien plus simple que prevu, contrairement
a ce que supposait la note precedente.

Piege bloquant identifie : la BnF indexe l'ISBN tel qu'imprime, donc les
livres d'avant 2007 ne sont trouvables que par leur ISBN-10, alors que le
scanner lit toujours un EAN-13. Mesure sur echantillon verifie via
OpenLibrary : 2 titres sur 3 introuvables en ISBN-13 mais trouves en
ISBN-10. Interroger systematiquement les deux formes.

Autres pieges confirmes et documentes :
- aucune couverture cote BnF -> passer par OpenLibrary, avec
  `?default=false` sans lequel un placeholder masque les absences
- plusieurs notices possibles pour un meme ISBN (3 pour Germinal)
- ponctuation ISBD a nettoyer sur titre / auteur / editeur, regles validees
- livres etrangers absents, ce qui confirme la necessite de la cascade

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:27:40 +02:00
mathieuandClaude Opus 5 a5392cedac Acter les cinq decisions structurantes (offline, portee, ISBN, SSO, ebooks)
- Hors-ligne : consultation seule via cache des reponses GET. SQLite WASM
  cote client ecarte. L'UI devra desactiver explicitement les ecritures
  hors-ligne plutot que de les laisser echouer.
- Portee des donnees : collection commune au foyer. `UtilisateurId`
  (proprietaire) devient `AjoutePar` (tracabilite) — ne jamais filtrer les
  lectures dessus.
- Sources ISBN : BnF (SRU) en principale, OpenLibrary en secours. Motif :
  OpenLibrary est lacunaire sur le fonds francais. API BnF pas encore
  testee, a valider avant implementation.
- SSO : en-tetes SSOwat `YNH_USER` / `YNH_USER_EMAIL` / `YNH_USER_FULLNAME`.
  OIDC ecarte, non documente par YunoHost (verifie ce jour). Corrige au
  passage le nom d'en-tete, qui n'est pas `Remote-User`.
- Ebooks : fiches uniquement, pas de stockage de fichiers. Les prets ne
  concernent donc que les livres physiques.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:21:38 +02:00
mathieuandClaude Opus 5 d56061db6b Sortir les sources du tarball et acter ZXing.Net pour le scan ISBN
Le dépôt ne versionnait qu'une archive `mabibli-init.tar.gz`, ce qui
empêchait git de suivre le contenu des fichiers. Les trois fichiers sont
désormais à plat et l'archive est supprimée.

Scan ISBN : remplacement de `html5-qrcode` par ZXing.Net (C#, Apache 2.0).
Validé concrètement en .NET 10 — round-trip EAN-13 OK, publish Blazor WASM
OK, 0,53 ms/frame dans le pire cas (échec sur frame 640x480 bruitée),
+192 Ko brotli sur le payload. Motif principal : le décodage reste
réutilisable hors navigateur si le projet évolue en scanner de
bibliothèque.

Clarification du besoin hors-ligne : consultation de la bibliothèque
existante uniquement. Signale au passage que SQLite vit côté serveur et
qu'un cache client sera nécessaire — point d'architecture non résolu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:01:46 +02:00
mathieu 36cd4a4559 Initial project structure with requirements and Claude Code context 2026-08-17 20:48:40 +02:00