ÉTAT DU PROJET — prototype-web
=================================
Fichier principal : prototype-web/index.html (environ 1,64 Mo)
Tests : node tests/tous.mjs
        latex.test.mjs     39/39 OK
        structure.test.mjs 88/88 OK
        e2e.mjs            323/323 OK

POURQUOI LA PAGE PÈSE 1,6 Mo
=============================
Le moteur de lecture PDF (pdf.js 3.11.174, licence Apache-2.0) est embarqué
EN LIGNE dans la page, avec son bloc ouvrier. La page reste donc autonome :
aucune ressource distante, aucun fichier voisin, et l'import de PDF marche
par double-clic, en file://.

FONCTIONNALITÉS DÉJÀ OK
========================
- Copier (Ctrl+C) et Coller (Ctrl+V, bouton #btn-paste) depuis le presse-papiers
- Palette d'outils flottante repliable/déplaçable, 3 colonnes, icônes Lucide
- Outils de dessin : Sélection, Stylo, Pinceau, Ligne, Flèche, Double flèche,
  Flèche courbe, Rectangle, Cercle, Ellipse, Page quadrillée, Pot de peinture,
  Gomme, Capturer une zone, Zoom sur une zone
- Widgets : Horloge numérique, Horloge à aiguilles, Minuteur (multiple)
- Découper une image en carreaux (traits verticaux/horizontaux)
- Formule LaTeX, Mots déplaçables, Glisser-copier par objet
- Supprimer, Tout sélectionner, Mélanger, Grouper/Dégrouper, Fixer
- Copier / Coller le lot sélectionné (boutons de la barre du bas)
- Diapositives : ranger en tirant la vignette, ajouter/dupliquer/supprimer par
  vignette
- Export PDF (window.print), Export HTML autonome
- Largeur du trait (range 1-24), épaisseur de la gomme (4-120)

CE QUI A ÉTÉ FAIT DANS CETTE PASSE
==================================

1. Plan vérifié : la seule fonctionnalité documentée et non codée était
   l'import PDF. Elle est maintenant développée (voir 3.).

2. Agrandir un objet ou une image (demande utilisateur)
   - La poignée du coin SUIT LE POINTEUR dans toutes les directions : tirer
     vers le bas agrandit autant que tirer vers la droite. Avant, seule la
     course horizontale comptait : le geste « prolongeait » l'objet dans un
     seul sens et paraissait ne rien faire vers le bas.
   - Le geste est projeté sur la diagonale de l'objet, qui garde donc
     toujours ses proportions (l'image n'est jamais étirée).
   - Vérifié de bout en bout : un geste VERTICAL agrandit les deux côtés, et
     le rapport largeur/hauteur ne bouge pas.

3. Import d'un PDF en diapositives (le point « À FAIRE » du plan précédent)
   - Bouton #btn-import-pdf dans la barre du haut, modale #modale-pdf avec
     champ de fichier #champ-pdf (accept="application/pdf", plusieurs fichiers).
   - Chaque page du PDF est rendue dans un canvas, à l'échelle qui la fait
     tenir entièrement dans la diapositive (1280 × 720), puis devient une image
     posée sur une NOUVELLE page. Les diapositives arrivent juste après la page
     courante et l'on bascule sur la première.
   - Le moteur est embarqué (#script-pdfjs et #script-pdfjs-worker) ; le bloc
     ouvrier est chargé comme un script classique, si bien que pdf.js travaille
     sur le fil principal : aucun fichier ouvrier à charger, ce qui est
     indispensable en file://.
   - Échec (PDF illisible, chiffré) : message dans la barre du bas, jamais
     par-dessus le tableau.

4. Outil de capture d'une zone (demande utilisateur)
   - Nouvel outil « capture » dans la palette (icône viseur teintée).
   - On encadre une zone : elle est recopiée TELLE QU'ELLE SE VOIT (objets
     dessinés, images, textes, formules, horloges) dans un SVG foreignObject,
     peint dans un canvas (×2 pour la netteté), et devient un PNG posé au même
     endroit, tout de suite sélectionné.
   - Aucune bibliothèque, aucun partage d'écran, aucune requête réseau.
   - Vérifié : l'image contient vraiment l'encre du dessin (comptage de pixels)
     et elle est posée aux coordonnées et à la taille de la zone.
   - La capture rend la main à l'outil SÉLECTION en laissant la pièce choisie :
     on la déplace tout de suite, sans rappeler l'outil.

5. Bug réel corrigé : le bouton « Exporter en PDF » ne faisait RIEN
   - Sa ligne de branchement avait été avalée par le commentaire voisin
     (« // Exports    $('#btn-pdf').addEventListener(...) »), donc le clic
     n'était plus câblé. Le test e2e attribuait l'échec au navigateur headless :
     c'était un vrai bug. Corrigé, et un contrôle de structure interdit
     désormais qu'un bouton soit branché dans un commentaire.
   - Correction d'un test e2e obsolète : il pilotait encore les champs RVB
     supprimés (champ-couleur-rvb, champ-interieur-rvb) et laissait la page
     d'essai en place, ce qui faisait échouer le comptage de pages de
     l'impression. Les trois échecs annoncés comme « headless » sont donc
     expliqués et levés : la suite e2e passe entièrement.

6. Images et pièces capturées vraiment redimensionnables (demande utilisateur)
   - Cause trouvée : les poignées étaient ajoutées DANS la balise <img>, qui ne
     peut rien contenir — elles n'étaient donc jamais affichées. Une image ne
     pouvait ni être agrandie, ni pivotée ; une pièce capturée encore moins.
   - Chaque image est désormais enveloppée dans une BOÎTE (.item-image) qui
     porte la taille, la rotation et les poignées ; l'image la remplit.
   - Vérifié à la vraie souris : poignée réellement visible, agrandissement et
     rotation d'une image, et agrandissement d'une pièce juste capturée.

7. « Mettre en fond » devient un CALQUE À PART (demande utilisateur)
   - Vérification d'abord : l'ordre de peinture fonctionnait déjà (test de
     peinture réel : deux formes opaques superposées, on mesure le pixel
     commun avant/après). Le problème était ailleurs.
   - D'où la réponse retenue : un calque de fond SÉPARÉ du calque des objets,
     peint dessous. Le fond est BLANC au départ (rien à voir tant qu'on n'y met
     rien), et « Mettre en fond » y range la sélection.
   - Le calque reste HORS des commandes de lot : Ctrl+A, le lasso et Mélanger
     ne l'emportent plus — une image de fond ne part donc jamais par accident.
   - Le bouton est un INTERRUPTEUR : un second clic ressort le lot du fond.
   - Un objet du fond reste ATTRAPABLE par son encre (une forme ou un trait ne
     prend le clic que par son encre), même sous un cadre vide.
   - Vérifié : peinture réelle, rangement dans le calque, sortie au second
     clic, mise à l'écart par Ctrl+A / lasso / Mélanger, reprise au clic.

8. Outil « Zoom sur une zone » (demande utilisateur)
   - Même geste que la capture (on encadre), mais la pièce part dans une
     NOUVELLE diapositive, juste après la page courante.
   - Elle y est posée au DOUBLE de la taille de la zone (le rendu de la zone
     est déjà fait à ×2 : on le pose à sa taille naturelle) et CENTRÉE.
   - La pièce reste une image ordinaire : la Sélection reprend la main, on
     peut l'AGRANDIR ENCORE à la poignée, la déplacer, la recadrer.
   - Vérifié : nouvelle diapositive, devient la page active, zone 400 × 300
     → pièce 800 × 600 centrée, le DÉTAIL vraiment repris (comptage de pixels :
     de l'encre, pas du blanc), Sélection rendue, agrandissement encore
     possible, puis nettoyage des pages d'essai.

9. Tests ajoutés / étendus
   - structure : import PDF câblé, capture et zoom câblés, moteur PDF embarqué
     et inerte (aucun <script>, aucun <link>, aucun chargement réseau), teintes
     des nouveaux outils, aide et branchements des boutons, calque de fond.
   - e2e : agrandissement vertical qui garde les proportions, capture vérifiée
     pixel par pixel, import d'un vrai PDF minuscule (fichier écrit sur le
     disque, donné au champ via CDP), calque de fond (peinture réelle, hors
     commandes de lot), images redimensionnables, zoom en nouvelle diapositive.
   - Le test de l'export PDF s'appuie sur Object.defineProperty : en headless,
     une simple affectation de window.print ne prenait pas.

PASSE SUIVANTE (mêmes jours, demandes de l'utilisateur)
======================================================

10. BUG RÉEL : le bouton « Coller » ne collait JAMAIS
    - Le bouton naissait `disabled` dans la page, et RIEN ne le rallumait :
      après un Ctrl+C sur un objet de la page, il restait éteint et inerte.
    - Corrigé : majPressePapiers() décide de son état (allumé dès qu'un objet
      est copié, et la consigne dit alors combien d'objets il va reposer).
    - Contrôle de structure ajouté pour TOUTE cette famille de bugs : un bouton
      écrit `disabled` dans la page doit avoir, dans le code, la ligne qui
      décide de son état.

11. Outil « Flèche courbe » (demande utilisateur)
    - Le geste du stylo, plus une pointe au bout : la flèche qui tourne.
    - La pointe s'aligne sur la direction GÉNÉRALE de la fin du tracé
      (pointEnAmont), pas sur le dernier micro-segment.
    - La gomme emporte la pointe si elle coupe le bout, la laisse sinon : le
      morceau qui garde l'extrémité du tracé garde la flèche.

12. « Ajouter une page » à côté de « Dupliquer la page » (demande utilisateur)
    - Chaque vignette porte maintenant trois boutons alignés : ajouter,
      dupliquer, supprimer — l'ajout pose une page vierge juste après CETTE
      page. Dans la barre du haut, ajouter est déjà à côté de dupliquer.

13. Ranger les diapositives en les TIRANT, plus avec les flèches (demande
    utilisateur)
    - Les deux petits boutons ↑ ↓ du rail ont été retirés : ils demandaient de
      viser pour un geste d'un seul mouvement, et ne disaient pas où la page
      allait atterrir.
    - On tire la vignette : elle s'efface sur place, un repère dit où elle va,
      et au relâchement la page prend cette place et reste la page courante.
    - Le dépôt se calcule sur le MILIEU DE LA VIGNETTE (l'image de la
      diapositive), pas sur le bloc entier, qui traîne sa rangée de boutons.
    - Le suivi écoute la FENÊTRE et non la vignette (le doigt en sort), et
      n'utilise pas la capture de pointeur, qui n'existe pas partout.
    - Un simple clic continue de changer de page : le rangement se garde d'un
      clic parasite par une fenêtre de 400 ms après le dépôt.
    - Au clavier : Alt + ↑ / ↓ sur la vignette au focus.

14. Tests ajoutés
    - structure : flèche courbe câblée, bouton Coller suivi, rangement au
      glissement, bouton jamais figé en `disabled`, et « ajouter » juste avant
      « dupliquer » dans la vignette.
    - e2e : flèche courbe tracée d'un vrai geste COURBE (deux traits de pointe
      posés au bout du tracé) avec son témoin (un trait au stylo n'a aucune
      pointe), clic réel sur « Coller » (le bouton s'allume, la copie arrive à
      24 px), et tirage d'une vignette sur une autre puis retour.

PASSE SUIVANTE (demandes de l'utilisateur)
=========================================

15. « Grouper » : plusieurs objets deviennent UN SEUL objet
    - Nouveau bouton #btn-grouper dans la palette (icône cadre + deux carrés).
    - Un GROUPE est un objet de la page : type 'groupe', boîte (x, y, l, h),
      taille NATURELLE (nl, nh), angle, et `enfants` — les objets d'origine,
      recopiés, en coordonnées RELATIVES au groupe.
    - Le groupe se DÉPLACE d'un bloc (les enfants suivent sans rien recalculer),
      s'AGRANDIT (une mise à l'échelle du conteneur interne : textes et formules
      grandissent aussi) et PIVOTE (l'angle du groupe). 'groupe' rejoint donc
      TYPES_A_TAILLE.
    - Le groupe s'attrape par ses enfants comme par sa boîte : `closest`
      remonte de l'enfant au `.item-groupe`, jamais à l'enfant seul.
    - Le bouton est un INTERRUPTEUR : allumé sur un groupe, il annonce Dégrouper
      et rend ses enfants à leur place EXACTE (échelle et rotation comprises).
      Raccourci Ctrl+G.
    - Une copie de groupe (presse-papiers, glisser-copier, duplication de page)
      emporte ses enfants RECOPIÉS, avec des identifiants neufs — `copierItem`
      descend maintenant dans `enfants`.
    - L'ancien CALQUE DE FOND est supprimé : plus de `calque-fond`, plus de
      drapeau `fond`, plus de `conteneurDe`.

16. « Mettre en fond » devient « FIXER » (demande utilisateur)
    - Nouveau bouton #btn-fixer (icône cadenas), à la place de l'ancien.
    - Un objet fixé RESTE PEINT à sa place, dans le même plan : l'ordre
      d'affichage ne change pas. Mais plus rien ne le prend — ni le clic, ni
      Ctrl+A, ni le lasso, ni Mélanger, ni Grouper.
    - C'est DÉFINITIF : pour ne plus le voir, il faut dessiner PAR-DESSUS et
      fixer à son tour ce qui le recouvre. Seule l'annulation (Ctrl+Z) libère.
    - La garde est dans le CODE (`if (item && !item.fige)`), pas seulement dans
      la feuille de style : une forme reste attrapable par son encre, un objet
      fixé ne doit donc pas l'être malgré cela.

17. La diapositive courante se repère d'un coup d'œil (demande utilisateur)
    - Le contour était un simple filet bleu d'un pixel, noyé parmi les autres.
    - Il est maintenant ÉPAIS (4 px), doublé d'un liseré clair et d'un halo, et
      le numéro de la page passe à l'accent.

18. Tests
    - structure : le test du calque de fond devient deux tests (Grouper,
      Fixer), deux outils attendus de plus, et les teintes des nouveaux
      boutons. 85 contrôles.
    - e2e : le scénario du calque de fond devient (a) grouper deux objets —
      même place et même taille pour les enfants, déplacement d'un bloc,
      agrandissement d'un seul tenant, dissociation — et (b) fixer un disque
      (il ne s'attrape plus, il reste peint, un cache par-dessus, hors des
      commandes de lot, Ctrl+Z le libère) ; sur les deux rectangles opaques, il
      vérifie que « Fixer » ne change PAS le plan — contrairement à l'ancien
      « mettre en fond » — et qu'un cache posé dessus le recouvre. 306 contrôles
      — portés depuis à 86 et 309 par l'item 19.

19. La barre du haut : un bouton, une couleur (demande utilisateur)
    - Reproche : « les boutons en haut sont trop sobres et se ressemblent ».
      Juste : la teinte ne colorait que l'ICÔNE, le corps du bouton restait le
      même gris pour les onze — « annuler » et « refaire », « ajouter » et
      « dupliquer » étaient interchangeables à l'œil.
    - La recette de la palette passe donc au corps du bouton : fond ET bordure
      tirés de la teinte (.barre .btn-icone), icône à 18 px, halo au survol
      (box-shadow avec la teinte) et léger soulèvement.
    - Une teinte PAR bouton, jamais deux fois la même : ambre pour annuler,
      turquoise pour refaire, émeraude pour ajouter une page, lime pour
      dupliquer, rose pour supprimer, violet pour la formule, bleu pour
      l'image, rose pour le PDF importé, ciel pour l'aide, or pour l'export.
      L'export PDF garde la seule couleur d'accent, et ses règles sont
      déclarées APRÈS celles de teinte pour ne pas se faire recouvrir.
    - Éteint, un bouton GARDE sa couleur et s'estompe (opacité déjà en place) :
      trois rectangles gris de plus, c'était le reproche. Annuler, refaire et
      supprimer une page naissent éteints : ils restent reconnaissables.
    - Tests : structure — les dix teintes de la barre existent, sont toutes
      différentes, dérivent fond/fond-fort/bordure, et le bouton d'accent est
      bien déclaré en dernier. 86 contrôles.
    - Tests : e2e — la page est ouverte pour de bon, et l'on compare les fonds
      CALCULÉS des onze boutons (aucun gris commun), plus le contraste de
      chaque icône sur son propre fond teinté (≥ 3:1). 309 contrôles.

20. « Insérer une page quadrillée » dans la palette (demande utilisateur)
    - Nouvel outil #quadrillage (.outil[data-outil="quadrillage"], icône
      grille, teinte cyan #22d3ee), raccourci Q, fiche d'aide.
    - Le geste d'un outil de dessin, avec une différence : ENCADRER une zone
      donne une feuille de cette taille, arrondie au CARREAU ENTIER (une boîte
      de 150 x 105 devient 144 x 96, soit 6 x 4 carreaux de 24 unités) ; un
      simple CLIC, sans geste, couvre la page entière, centrée sur un nombre
      entier de carreaux (une bande de 4 px reste au bord plutôt qu'une
      dernière colonne tronquée).
    - C'est une FORME (type 'forme', forme 'quadrillage') : elle hérite donc de
      tout — déplacement, poignée de taille (les proportions sont conservées,
      donc les carreaux restent carrés), rotation, copie, presse-papiers,
      duplication de page, grouper, fixer, annuler, export, impression.
    - Le NOMBRE de carreaux est enregistré sur l'objet (colonnes/lignes), pas
      leur taille en pixels : agrandir la feuille AGRANDIT ses carreaux au lieu
      d'en semer d'autres, et un fichier exporté — qui redéduit la géométrie
      d'auteur de la boîte (nw supprimé pour les formes) — montre la même
      feuille que l'éditeur.
    - Du PAPIER, pas un calque : le fond est opaque (blanc), et la feuille
      arrive SOUS les objets déjà écrits (page.items.unshift + insertBefore sur
      le premier nœud du calque) — on dessine par-dessus. Elle reste un objet
      ordinaire : elle se choisit, se déplace, se fixe et s'annule.
    - Une forme FERMÉE de plus : le pot de peinture peut repeindre le papier, et
      la gomme entaille le cadre. L'outil n'affiche PAS le panneau Trait
      (couleur, épaisseur) : ses carreaux sont les mêmes pour tout le monde
      (24 unités, couleur #94a3b8, trait de 1).
    - Tests : structure — outil attendu, carreau nommé, nombre de carreaux sur
      l'objet (et aucune taille en pixels), pose au fond (ordre des objets ET
      ordre d'affichage), clic = page entière, forme fermée, pas de panneau
      inutile, raccourci Q. 88 contrôles.
    - Tests : e2e — sur une page neuve : Q choisit l'outil, le panneau Trait
      reste caché (il s'affiche pour le rectangle, témoin du contraire), un
      clic pose une feuille qui couvre la page (boîte entière de carreaux,
      papier opaque, 81 traits de carreaux), un geste de 150 x 105 donne
      EXACTEMENT 144 x 96 en (600, 200), la feuille est le PREMIER objet du
      calque (donc sous le trait déjà écrit), elle se choisit au clic comme un
      objet, l'export emporte les deux feuilles avec leurs colonnes/lignes et
      sans nw, et la page d'essai se retire. 323 contrôles.

RESTE À FAIRE (aucun point bloquant)
====================================
- L'import PDF lit les pages en images : on ne modifie pas le texte du PDF.
- Pas de barre de progression fine : l'avancement s'affiche page par page dans
  la modale.
- La capture s'appuie sur SVG foreignObject : vérifiée sur Edge/Chromium, les
  autres navigateurs n'ont pas été mesurés.
- Le moteur PDF embarqué est le seul code tiers de la page (~1,4 Mo) ; ses
  URI d'espaces de noms (XFA/XMP) et l'adresse de sa licence ne sont jamais
  chargées — un contrôle de structure le vérifie.

PROJET VALIDÉ
============================================
- Structure HTML valide
- tests/tous.mjs : aucune suite en échec (39 + 88 + 323 contrôles)
- La page reste autonome : aucune ressource distante, aucun fichier voisin
- Le fichier HTML exporté contient le moteur PDF et sait donc aussi importer

===========================================
FIN
===========================================
