Audit complet de mise en forme — Image (2e élément du tableau)
Implémente toutes les options manquantes identifiées pour l'élément Image : dimensionnement/ratio/object-fit, filtres CSS, upload de fichier (en plus de l'URL), lien/plein écran au clic, chargement différé, légende, et tous les attributs de boîte partagés déjà créés pour Titre/Paragraphe (padding/margin/fond/bordure/ombre/opacité/ position du bloc). Système de pages : un support peut désormais avoir 0 page (un nouveau support démarre vide), suppression de toutes les pages en un clic, et la pagination automatique insère intelligemment la nouvelle page juste après celle qui déborde plutôt qu'en toute fin de liste. Bugs réels trouvés et corrigés en cours de route : le style de bloc (dont align-self) ciblait l'élément interne au lieu de son enveloppe (légende/lien) ; une image à sa taille native pouvait déclencher une pagination infinie ; upload/mise à jour d'attribut ne déclenchaient jamais le contrôle de débordement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
6c7675fad0
commit
9ad50c58b8
@@ -64,6 +64,12 @@ image de fond, `overflow`/`z-index`/position absolue, transitions au
|
||||
survol, réglages différents par taille d'écran (responsive), arrondi
|
||||
par coin (un seul rayon pour les 4 coins, comme l'étiquette).
|
||||
|
||||
**Changement rétroactif (au moment de l'Image, ci-dessous)** : `width`
|
||||
(largeur fixe) a été ajouté à `box_style.py` partagé — Titre/Paragraphe
|
||||
gagne donc aussi ce réglage a posteriori (en plus de la largeur MAXIMALE
|
||||
déjà là), sans repasser par une validation dédiée puisque c'est un ajout
|
||||
pur (aucun comportement existant modifié).
|
||||
|
||||
Bugs transversaux trouvés et corrigés pendant ce chantier (concernent
|
||||
TOUT l'éditeur, pas seulement Titre/Paragraphe) :
|
||||
- Panneau Propriétés jamais reconstruit après un clic (boutons/segments
|
||||
@@ -74,7 +80,166 @@ TOUT l'éditeur, pas seulement Titre/Paragraphe) :
|
||||
à tort par les cases à cocher et curseurs, cassant leur apparence
|
||||
native.
|
||||
|
||||
### 2. Image — à faire (prochain élément)
|
||||
### 2. Image — ✅ audité et validé (commité)
|
||||
|
||||
Implémenté : légende (`caption`, enveloppe dans `<figure>/<figcaption>`),
|
||||
ajustement dans son cadre (`object_fit` : taille réelle/couvrir/
|
||||
contenir/étirer), format/ratio (`aspect_ratio` : libre/carré/4:3/16:9),
|
||||
filtre (3 préréglages : noir et blanc/sépia/flouté), chargement différé
|
||||
(`loading="lazy"`), comportement au clic mutuellement exclusif (aucun /
|
||||
lien externe dans un nouvel onglet / plein écran — un vrai overlay
|
||||
plein écran côté client, `forgeDocOpenImageLightbox`), tous les
|
||||
attributs de boîte partagés (dont la nouvelle **largeur fixe**, ajoutée
|
||||
à `box_style.py` à cette occasion).
|
||||
|
||||
Comme pour les mini-jeux et la pièce jointe d'un bouton, le lien et le
|
||||
plein écran ne sont réellement cliquables qu'en Mode Aperçu — en
|
||||
édition, le clic sélectionne l'élément (la navigation native du lien
|
||||
est bloquée pour ne pas quitter l'éditeur par accident).
|
||||
|
||||
**Ajout (retour utilisateur : "il manque la possibilité d'uploader une
|
||||
image")** : téléversement d'un fichier depuis l'ordinateur (PNG/JPG/
|
||||
GIF/WEBP/SVG), en plus du champ URL externe déjà là — mirroir exact du
|
||||
mécanisme déjà en place pour la pièce jointe d'un bouton :
|
||||
`routes/document/document_element_upload_image.py` (stocke sous
|
||||
`uploads/`, jamais `attachments/` — pas de `as_attachment`, l'image doit
|
||||
s'afficher, pas se télécharger) et `routes/document/
|
||||
document_uploaded_file.py` (route de service dédiée). Le fichier
|
||||
téléversé vide `svg_markup` au passage (les deux modes ne coexistent
|
||||
jamais). Le panneau propose maintenant les deux : sélecteur de fichier
|
||||
(natif, pas de champ texte) en premier, champ URL externe en second
|
||||
pour une image déjà hébergée ailleurs.
|
||||
|
||||
Volontairement laissé de côté (complexité/valeur douteuse pour du
|
||||
contenu de formation) : `mix-blend-mode`, découpe `clip-path`, overlay
|
||||
au survol, arrondi par coin, réglages responsive par taille d'écran.
|
||||
|
||||
Bug pré-existant corrigé au passage : `docImageSrc`/`docImageAlt`
|
||||
n'étaient pas échappés avant insertion dans l'attribut `value` du
|
||||
panneau Propriétés (mineur, mais corrigé puisque cette fonction était
|
||||
déjà réécrite).
|
||||
|
||||
**Bug réel corrigé (retour utilisateur : "la position de bloc ne
|
||||
fonctionne pas sur l'image")** : `render_box_style` (padding/margin/
|
||||
fond/bordure/largeur/position du bloc, dont `align_self`) était
|
||||
appliqué à l'`<img>`/`<div>` INTERNE, jamais à son enveloppe
|
||||
(`<figure>`/`<a class="docImageLink">`/`<div class=
|
||||
"docImageLightboxTrigger">`) quand une légende ou un comportement au
|
||||
clic en ajoutait une — `align-self` posé sur un simple descendant du
|
||||
flex-item n'a aucun effet CSS, d'où le bouton "position du bloc" sans
|
||||
effet visible dès qu'une légende ou un lien était configuré. Corrigé
|
||||
dans `_render_image` (`document_engine/rendering/
|
||||
render_document_element.py`) : le style de bloc cible désormais
|
||||
toujours l'élément réellement top-niveau (enfant direct de
|
||||
`.docPageContent`), quel que soit l'emboîtement. Changement de
|
||||
comportement réel sur du contenu existant : une image avec légende/lien
|
||||
et un fond/une bordure/un padding déjà réglés les verra désormais
|
||||
appliqués à TOUT le bloc (image + légende), pas seulement à l'image —
|
||||
c'est le comportement correct/attendu, mais je le signale car ça change
|
||||
le rendu visuel d'éléments déjà créés.
|
||||
|
||||
**Bug réel corrigé (retour utilisateur : "quand j'ajoute une image ça
|
||||
crée des pages à l'infini")** : une photo importée à sa taille native
|
||||
pouvait dépasser une page entière à elle seule ; la pagination
|
||||
automatique (`forgeDocCheckPageOverflow`, static/document/js/
|
||||
document-editor.js) la déplaçait alors sans fin vers une page neuve,
|
||||
qui débordait identiquement. Corrigé une première fois par un garde-fou générique (si tout le
|
||||
contenu de la page déborde déjà, rien à répartir, on arrête) + une
|
||||
hauteur maximale par défaut sur `img.docImage` (plafonnée à la hauteur
|
||||
intérieure de la page, `static/document/document-editor.css`).
|
||||
|
||||
**Affiné ensuite (retour utilisateur : "si l'image uploadée est trop
|
||||
grande je préfère qu'elle soit redimensionnée plutôt que bouger sur une
|
||||
autre page")** : `forgeDocCheckPageOverflow` (static/document/js/
|
||||
document-editor.js) évite de paginer une image qui déborde quand il
|
||||
reste assez de place pour un résultat encore utilisable — elle est
|
||||
rétrécie SUR PLACE (`max-height` posé en style inline,
|
||||
calculé à partir de l'espace réellement disponible sous elle sur SA
|
||||
page actuelle, légende comprise) à chaque rafraîchissement du canevas.
|
||||
Purement visuel, jamais persisté en attribut (l'espace disponible
|
||||
dépend du contenu au-dessus, qui change en éditant). Un réglage
|
||||
explicite de hauteur maximale via le panneau reste prioritaire (une
|
||||
valeur inline posée par un attribut serait recalculée par-dessus à
|
||||
l'affichage suivant si elle déborde encore). Le garde-fou anti-boucle
|
||||
et le défaut CSS restent en place pour les AUTRES kinds et comme filet
|
||||
de sécurité au tout premier rendu (avant que le JS n'ait tourné).
|
||||
|
||||
**Changement de conception (retour utilisateur : "ce cadre ne devrait
|
||||
pas changer de taille en fonction de la taille de l'image mais être
|
||||
fixe et contraindre l'image dedans")** : plutôt que de rétrécir
|
||||
dynamiquement une image trop grande selon l'espace disponible (approche
|
||||
fragile, source des deux bugs ci-dessus), un nouvel élément image a
|
||||
maintenant un cadre FIXE dès sa création — `object_fit="cover"` +
|
||||
`height="220px"` par défaut au lieu de vides (`document_engine/labels/
|
||||
element_kind_labels.py`) — une photo importée est donc TOUJOURS rognée
|
||||
pour remplir ce cadre, quelle que soit sa résolution native. "Taille
|
||||
réelle" reste un choix explicite possible dans le panneau (segmented
|
||||
"Ajustement dans son cadre"). **Ne s'applique qu'aux NOUVEAUX éléments
|
||||
image** — un élément déjà créé avant ce changement garde ses attributs
|
||||
`object_fit`/`height` existants (jamais re-migré automatiquement,
|
||||
`sanitize_element_attributes` ne touche pas au kind "image", voir sa
|
||||
docstring) ; pour en faire bénéficier une image déjà présente, régler
|
||||
manuellement "Couvrir" + une hauteur via le panneau. Le rétrécissement
|
||||
dynamique (garde-fou anti-boucle + `forgeDocCheckPageOverflow` appelé
|
||||
après upload/mise à jour d'attribut) reste en place comme filet de
|
||||
sécurité pour une hauteur explicite déraisonnablement grande.
|
||||
|
||||
**Bug réel corrigé une seconde fois (retour utilisateur : "ce n'est pas
|
||||
redimensionner")** : le rétrécissement ci-dessus vit dans
|
||||
`forgeDocCheckPageOverflow`, appelée uniquement par
|
||||
`forgeDocRefreshCanvas` (ajout/déplacement/suppression/Annuler-
|
||||
Rétablir) — mais **téléverser un fichier dans une image existante**
|
||||
(`forgeDocApiUploadImage`) et **changer un attribut quelconque**
|
||||
(`forgeDocUpdateAttributes`, le point d'entrée central de tous les
|
||||
panneaux) patchent chacun le DOM directement, SANS jamais passer par
|
||||
ce chemin : le rétrécissement ne se déclenchait donc jamais après un
|
||||
upload. Corrigé en appelant explicitement `forgeDocCheckPageOverflow()`
|
||||
à la fin de ces deux fonctions. Pas de test automatisé possible ici :
|
||||
aucune suite de tests n'existe pour `document-editor.js`
|
||||
(`package.json` ne couvre que `static/game/js/`), vérification
|
||||
manuelle uniquement.
|
||||
|
||||
**Affiné une troisième fois (retour utilisateur : "l'ajout de page
|
||||
quand le contenu déborde dois rester et même être plus intelligent, la
|
||||
nouvelle page dois etre sous la page qui deborde meme si ya d'autre
|
||||
page")** : la pagination automatique (pour tout kind, pas seulement les
|
||||
images désormais épargnées ci-dessus) insérait toujours la page neuve
|
||||
en toute fin de la bande d'onglets (`add_document_page` l'ajoute
|
||||
toujours à la fin), même si d'autres pages existaient déjà après celle
|
||||
qui déborde — déborder sur la page 2 d'un support qui en compte 5
|
||||
ajoutait la nouvelle page en position 6 au lieu de 3. Corrigé dans
|
||||
`forgeDocCheckPageOverflow` : la page est créée puis immédiatement
|
||||
déplacée (`forgeDocApiPageMove`, mécanisme déjà existant pour le
|
||||
glisser-déposer du panneau Pages) juste après la page active, décalant
|
||||
les pages suivantes d'un cran. Extraction d'un helper partagé
|
||||
`forgeDocReorderLocalPage` (état client après un déplacement) réutilisé
|
||||
par le glisser-déposer ET par ce nouveau cas, pour ne pas dupliquer ce
|
||||
calcul. Vérification côté serveur déjà couverte par les tests existants
|
||||
de `move_document_page`/`document_page_move` (l'insertion "au milieu"
|
||||
de la bande d'onglets y est déjà testée) ; le déclenchement côté client
|
||||
reste manuel faute de suite de tests JS.
|
||||
|
||||
**Affiné une quatrième fois** : sous un seuil de place restante
|
||||
(`FORGE_DOC_MIN_IMAGE_HEIGHT`, 60px) il n'y a plus de place RÉELLE sur
|
||||
la page (pas seulement pour cette image) — l'image bascule alors dans
|
||||
la pagination normale ci-dessus au lieu d'être rétrécie à une taille
|
||||
inutilisable, ce qui empêchait sinon une page pleine d'images
|
||||
d'enchaîner sur une nouvelle page. Vérifié par un test de bout en bout
|
||||
en conditions réelles (navigateur automatisé Playwright contre le
|
||||
serveur local, sur un support jetable créé puis supprimé pour
|
||||
l'occasion) : 5 images vides ajoutées à la suite produisent bien 3
|
||||
pages, chacune avec le cadre fixe "Couvrir" actif par défaut.
|
||||
|
||||
**Audit final** : chaque ligne du tableau d'audit initial pour
|
||||
l'élément Image est couverte — implémentée (dimensionnement/ratio/
|
||||
object-fit, bordure+ombre via `box_style.py`, filtre, lien/lightbox/
|
||||
lazy-load/légende, position du bloc dont l'alignement, upload de
|
||||
fichier) ou explicitement différée ci-dessus avec sa raison
|
||||
(`object-position`, `mix-blend-mode`, `clip-path`, overlay au survol,
|
||||
arrondi par coin, responsive par taille d'écran — mêmes exclusions que
|
||||
Titre/Paragraphe, valeur douteuse pour du contenu de formation). Rien
|
||||
d'oublié constaté à cette relecture. Validé par l'utilisateur, prêt à
|
||||
committer.
|
||||
|
||||
### 3. Bouton — à faire
|
||||
|
||||
|
||||
Reference in New Issue
Block a user