Trois retours distincts :
- Plus de présélection du thème déjà appliqué à l'ouverture — un choix
toujours explicite de l'utilisateur.
- L'état vide (.docTemplatePreviewEmpty) restait visible EN MÊME TEMPS
que l'iframe une fois un thème sélectionné : `display:flex` posé
directement dessus battait le `display:none` natif de [hidden]
(même bug déjà rencontré pour .docSidebarTabPanel[hidden] plus tôt
dans le projet) — les deux se partageaient flex:1, coupant l'aperçu
en deux au lieu de lui laisser tout l'espace.
- L'aperçu ne montrait que la première page du modèle sans aucun moyen
d'en voir les autres : la route /document/<slug>/theme/<id>/preview
rend désormais TOUTES les pages, une barre Précédent/Suivant
(entièrement côté client, aucun aller-retour serveur supplémentaire)
permet de naviguer entre elles.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Demande explicite : la modale occupe maintenant tout l'écran (100vw/
100vh, plus de padding/coins arrondis hérités de la modale générique),
divisée en deux colonnes strictement égales (grid-template-columns:
1fr 1fr) — la liste des thèmes devient une grille de cartes à gauche,
l'aperçu occupe toute la colonne de droite.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Deux bugs distincts :
- #docTemplateModal n'était jamais ajoutée au sélecteur qui définit les
tokens --doc-* (--doc-bg-2/--doc-border/--doc-text/...) — ces tokens
n'existaient nulle part sur elle, donc toutes les couleurs de fond de
la modale résolvaient à rien (transparence totale). Corrigé en
l'ajoutant à ce sélecteur (et au data-theme posé par
forgeDocApplyTheme, pour suivre le thème clair/sombre de l'éditeur).
- .docModalDialog--wide n'avait qu'un max-height, jamais un height
explicite : le dialogue flex se limitait à la hauteur naturelle de
son contenu, et l'iframe d'aperçu (flex:1) retombait à sa hauteur
intrinsèque minuscule faute de référence pour se déployer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Nouveau bouton dans le topbar de l'éditeur, à côté d'Aperçu, qui ouvre
une modale listant les thèmes du catalogue (document_engine/themes/).
Cliquer un thème charge un VRAI aperçu (rendu serveur réel dans un
iframe, jamais une resucée CSS côté client) avec le choix de garder le
contenu actuel ou de le remplacer par le contenu de démonstration du
modèle.
Architecture pensée pour une centaine de thèmes futurs : chaque thème
est une feuille de style externe (static/document/themes/<id>.css) qui
habille les classes fixes du moteur, jamais du code qui en changerait
la structure. Premier thème implémenté pour valider le mécanisme :
"Sécurité Incendie" (6 pages de contenu réel, quiz inclus).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contenu et mécanisme uniquement, aucun style ajouté (voir consigne du
24/09/2026) : deux nouveaux kinds de contenu (badge/carte, rendu en
div brutes sans CSS), un mode SVG inline pour l'image (svg_markup,
nettoyé par un nouveau sanitizer allow-list avant chaque rendu) et un
fichier téléchargeable joignable à un bouton (upload/download routes,
stockage sous db.support_dir). Le futur système de templates portera
l'habillage visuel de ces éléments.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur du 24/09/2026 : "dans la section contenu il manque
la possibilité d'utiliser une liste à puce ou ordonnée".
- Deux nouveaux kinds "liste_puces"/"liste_numerotee" dans
CONTENT_KINDS, chacun sélectionnable directement dans la bibliothèque
(comme titre/paragraphe/image/bouton) — partagent la même structure
d'attributs (`items`, une liste de chaînes), c'est le kind lui-même
qui décide <ul> ou <ol> au rendu (_render_list), pas un attribut
"ordered" redondant à tenir synchronisé.
- Rendu : un <li> par item, échappé (html.escape) comme tout le
contenu texte du document — une liste vide rend <ul>/<ol> sans
enfant plutôt qu'un placeholder (état normal, pas une image sans
fichier).
- Panneau Propriétés : même patron répéteur que l'Association/Memory
(ajouter/renommer/supprimer une ligne, rechargé après chaque
modification).
- Icônes de bibliothèque (☰/①) + style .docList (puces/numéros
visibles, espacement entre items).
6 nouveaux tests Python (attributs par défaut, rendu <ul>/<ol>,
échappement, liste vide, route d'ajout) + vérifié par un test jsdom
dédié contre un vrai support (bibliothèque, panneau Propriétés :
ajout/modification/suppression d'item réellement fonctionnels).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diagnostic console à l'appui (document.fullscreenElement confirmé
actif, .docEditor3/.docBodyWrap/.docCanvasArea mesurés à 960px =
window.innerHeight, mais .docPage seule à 678.78px) : ma précédente
tentative (width/height:100vh sur .docEditor3) ciblait le mauvais
niveau — toute la chaîne jusqu'à .docCanvasArea remplissait déjà
correctement l'écran. Le vrai plafond venait de max-height posé sur
.docPage pour le format A4 en édition (960 * 210/297 ≈ 678.79px,
exactement la valeur mesurée) : la règle Aperçu changeait bien
width/height/aspect-ratio mais oubliait max-height, qui continue de
gagner sur height:100% quelle que soit sa valeur. Neutralisé
(max-height:none, min-height:0) uniquement dans la règle Aperçu — le
plafond A4 reste actif en édition.
Vérifié par getComputedStyle (max-height résolu à "none" en Aperçu,
toujours actif en édition).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Capture utilisateur à l'appui du 24/09/2026.
- border-radius:0 sur toutes les cartes/boutons/cases du JOUEUR de
mini-jeu (.docMinigame, .docQuizCard/.docQuizResultCard,
.docQuizOption, .docQuizFeedback, .docQuizNextBtn/.docQuizRestartBtn,
.docAssocCard, .docAssocItem/.docAssocSlot,
.docMinigameRestartBar button, .docMemoryCardWrap/.docMemoryCardFace,
.docMotsCell/.docMotsWordChip) — "les bord des mini jeu ne doivent
plus etre arrondie". Le badge rond A/B (.docQuizOptionLetter, un
cercle volontaire) et le graphe d'édition du Scénario (outil de
configuration, pas le joueur) restent hors scope.
- Bug réel trouvé : .docQuizPlayerTitle/.docQuizQuestionText/
.docAssocTitle (et autres titres sans `color` propre) restaient
quasi invisibles sur la page blanche — `color` est une propriété
HÉRITÉE, et redéfinir la custom property --doc-text sur .docPage (fait
la session précédente) ne "recoupe" pas une couleur DÉJÀ CALCULÉE plus
haut sur .docEditor3 (palette sombre). Ajoute color:var(--doc-text)
explicitement sur .docPage, qui relance la résolution avec la bonne
valeur locale pour tout descendant sans `color` propre.
- La page ne remplissait pas toute la hauteur en Aperçu (bande noire en
bas) : .docEditor3 s'appuie sur flex:1 1 auto pour sa taille, valide
seulement comme enfant d'un flex container normal — une fois
réellement en plein écran (peint hors du flux normal par le
navigateur), ce mécanisme perd son contexte. width/height explicites
en secours sur .docEditor3:fullscreen et .docEditor3.docEditor3--preview.
Vérifié par getComputedStyle (border-radius à 0, --doc-text résolu en
sombre au niveau de .docPage, dimensions 100vw/100vh en preview).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur du 24/09/2026 : "le style des mini jeux dois etre
revue pour etre sur fond blanc" — les cartes/options/boutons des
mini-jeux (Quiz/Association/Memory/Mots mêlés/Scénario) s'appuient sur
les tokens --doc-* (--doc-card, --doc-bg-2, --doc-text, --doc-border,
succès/échec du Quiz), sombres par défaut et assortis au CHROME de
l'éditeur plutôt qu'à la page. Redéfinit ces tokens dans le scope de
.docPage avec les mêmes valeurs déjà établies pour
.docEditor3[data-theme="light"] (palette claire déjà conçue et
éprouvée dans ce fichier, pas une troisième version inventée) : la
page étant désormais toujours blanche, son contenu utilise toujours
cette palette, peu importe le thème choisi pour le chrome autour.
Vérifié par getComputedStyle (tokens résolus en valeurs claires dans
le scope de .docPage).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur du 24/09/2026 : "je veux que la page dans l'éditeur
et l'apercu soi blanche comme dans tous éditeur normale" — .docPage
utilisait var(--doc-card), qui suit le thème clair/sombre de
L'ÉDITEUR (chrome autour), pas de la page elle-même. Passe à #fff
fixe. Redéfinit --forge-text/--forge-text-muted dans le scope de
.docPage (le texte de contenu par défaut écrit color:var(--forge-text)
en style inline, qui vaut #e8ecf4 quasi blanc partout ailleurs dans le
site — illisible sur blanc sans cette redéfinition locale). Les
mini-jeux (options quiz, cartes association...) restent lisibles sans
changement : ils ont chacun leur propre fond sombre (--doc-bg-2),
indépendant du fond de la page. Vérifié par getComputedStyle.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Capture utilisateur à l'appui du 23/09/2026 : en ajoutant un 3e bloc,
les images (pourtant height:190px) rétrécissaient visiblement au lieu
d'aller sur une nouvelle page. Cause réelle : flex-shrink vaut 1 par
défaut pour tout enfant flex — une fois .docPage plafonnée en hauteur
(commit précédent), les enfants top-niveau de .docPageContent se
compressaient tous pour continuer à tenir, sans jamais réellement
déborder. Sans ce débordement réel, forgeDocCheckPageOverflow
(scrollHeight vs clientHeight) ne détectait jamais rien à paginer.
flex-shrink:0 sur .docPageContent > [data-element-id] : le contenu
garde sa taille naturelle et déborde franchement quand il n'y a plus
de place, ce qui déclenche la pagination automatique.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- .docPage n'avait que aspect-ratio pour dériver sa hauteur depuis sa
largeur — insuffisant en pratique : la page grandissait pour
accueillir tout le contenu au lieu de le clipser (overflow:hidden)
et laisser forgeDocCheckPageOverflow gérer la pagination (retour
utilisateur du 23/09/2026 : "le contenu ne dois jamais s'adapter
verticalement"). Ajoute un filet de sécurité : max-height calculé
explicitement (calc() à partir d'une nouvelle variable
--doc-page-width, source unique partagée avec width et le mode
aperçu à largeur fixe) + min-height:0 explicite, qui plafonnent
la hauteur quoi qu'il arrive.
- .docText n'avait aucune gestion de mot trop long sans espace —
débordait hors de la page au lieu de se couper (retour utilisateur
du 23/09/2026 : "les élément paragraphe ne vont pas a la ligne").
Ajoute overflow-wrap:break-word (jamais word-break:break-all, qui
casserait aussi les mots normaux sans raison).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Précision utilisateur du 23/09/2026 : pas les marges du canevas
(déjà annulées, "remet la page comme elle était") mais celles À
L'INTÉRIEUR de la page — le padding:24px posé sur .docQuizPlayer/
.docAssocPlayer/.docMemoryPlayer/.docMotsPlayer/.docScenarioPlayer
lors du centrage créait un espace visible entre le composant
(.docAssocCard etc.) et le bord de .docPage. Retiré : la carte touche
désormais les 4 bords de la page (son propre padding interne, 1.8rem,
reste intact pour la lisibilité de son contenu).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.docCanvasArea gardait son padding confortable (36px 40px) autour
d'une page mini-jeu en mode édition — seul .docPage lui-même (son
padding interne) avait été mis à plat jusqu'ici. Nouvelle règle
:has() conditionnée à la présence d'un mini-jeu, jamais globale : une
page de contenu normal garde ses marges habituelles. Vérifié par
getComputedStyle (0 avec mini-jeu, 36px 40px sans).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Mode Aperçu bascule désormais en VRAI plein écran (Fullscreen API,
forgeDocSetPreviewMode) au lieu d'un simple agrandissement CSS —
contourne d'un coup le clipping par overflow:hidden de main.content
documenté ailleurs dans ce fichier. .docTopbar est masqué comme le
reste du chrome ; un nouveau bouton flottant .docPreviewExitBtn
(visible seulement en Aperçu) permet de revenir à l'éditeur, en plus
d'Échap (natif, resynchronisé via fullscreenchange).
- .docCanvasArea perd son padding et .docPage abandonne son format A4
fixe en Aperçu (width/height:100%, aspect-ratio:unset) — le mini-jeu
remplit tout l'écran, sans bordure vide autour (retour utilisateur
du 23/09/2026 : "il doit prendre toute la place").
- Le contenu des cartes de mini-jeu (.docQuizCard/.docAssocCard/
.docMemoryCardWrap) passe de justify-content:center à flex-start —
aligné en haut, pas centré verticalement (retour utilisateur du
23/09/2026 : "le contenu aligner en haut").
Vérifié par getComputedStyle dans les deux modes (padding/aspect-ratio
de la page, visibilité du bouton de sortie et du bandeau, alignement
du contenu, gating pointer-events des mini-jeux — tout reste cohérent
simultanément).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le badge (.docMinigameBadge) restait visible au-dessus du joueur même
quand le mini-jeu occupe seul toute la page — retour utilisateur du
23/09/2026 : "ces éléments [...] doivent disparaitre et le composant
mini jeu pren toute la place". display:none (au lieu d'un simple
flex-shrink:0) : le joueur récupère toute la hauteur libérée. Vérifié
par getComputedStyle (badge display:none, joueur flex-grow:1,
gating pointer-events/plein-cadre de la page toujours corrects).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur du 23/09/2026 : une carte à largeur confortable
(même centrée) ne suffisait pas, "le mini jeu dois prendre toute la
hauteur et la largeur du document". Retire le max-width des cartes
internes (.docQuizCard/.docQuizResultCard/.docAssocCard/
.docMemoryCardWrap) — leur fond/bordure couvre désormais toute la
page (width:100% + align-items:stretch côté joueur pour la hauteur) —
et centre leur CONTENU à l'intérieur via display:flex +
justify-content:center sur la carte elle-même, plutôt que de le
laisser collé en haut d'une grande surface vide. Vérifié par
getComputedStyle (max-width devient bien "none", pointer-events et
plein-cadre de la page toujours corrects dans les deux modes).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le joueur (.docQuizPlayer/.docAssocPlayer/.docMemoryPlayer/
.docMotsPlayer/.docScenarioPlayer) remplissait déjà toute la hauteur
de la page (flex:1) mais sa carte interne restait en flux normal,
collée en haut-gauche — passage du joueur en display:flex + centrage,
avec une largeur confortable (max-width) sur les cartes internes
plutôt qu'un étirement bord à bord. Vérifié par getComputedStyle
(display:flex/centrage du joueur, max-width de la carte, gating
pointer-events et plein-cadre de la page toujours corrects).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le joueur réel (Quiz/Association/Memory/Mots mêlés/Scénario) n'est
plus display:none hors Aperçu — visible en permanence, édition ET
Aperçu, et occupe toute la page dans les deux modes (déjà garanti
depuis la règle plein-cadre :only-child, vérifié inchangé). Seule
l'interactivité (répondre/glisser/retourner une carte) reste réservée
au Mode Aperçu, via pointer-events:none par défaut / auto en Aperçu —
remplace l'ancien display:none/block qui masquait tout hors Aperçu.
Vérifié par getComputedStyle (display/pointer-events dans les deux
modes, padding/border-radius plein-cadre toujours à 0 en Aperçu).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- .docPage passe à une taille FIXE (960px, ratio A4 paysage 297:210
via aspect-ratio) au lieu de grandir avec le contenu, et
overflow:hidden — le contenu qui dépasse ne défile plus, au
créateur de le répartir sur une autre page (comme une vraie
diapositive, jamais de reflow automatique).
- Un mini-jeu ne peut plus partager sa page avec un autre élément, ni
l'inverse : vérifié côté serveur (routes/document/
document_element_add.py, point d'entrée unique de tout ajout),
jamais dupliqué côté client qui se contente d'afficher l'erreur
renvoyée (forgeDocApiAdd). Un mini-jeu ne peut pas non plus rejoindre
une rangée. 4 nouveaux tests de non-régression.
- CSS : quand un mini-jeu est l'unique enfant de la page
(.docPageContent > .docMinigame:only-child, invariant garanti par le
serveur), il s'étire en plein cadre (padding de la page à 0, coins
non arrondis, joueur en flex:1).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le <button> générique Bulma (static/style.css) pose
border-radius:var(--bulma-control-radius) sur TOUT bouton, jamais
annulé par notre seule déclaration border-bottom (la cascade CSS
s'applique propriété par propriété, pas règle par règle) : le trait
du bas remontait donc visiblement sur les côtés au lieu de rester
plat. Ajoute border-radius:0 explicite sur .docSidebarTab. Vérifié
via getComputedStyle (border-radius devient bien 0px).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Espace autour des rangées, notamment à droite pour ne pas coller la
barre de défilement fine au texte.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scrollbar-width:thin/scrollbar-color (Firefox/Chromium récents) +
::-webkit-scrollbar (WebKit/Blink plus anciens) sur .docPageManagerList
au lieu de la barre large par défaut du navigateur.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- "+ Ajouter une page" passe avant la liste et reste toujours visible
(flex-shrink:0), seule la liste défile désormais.
- Retire les boutons ↑/↓ par rangée : le glisser-déposer suffit pour
réordonner, ce qui laisse plus de place à l'affichage du nom de la
page. forgeDocMovePage devient mort (plus aucun appelant) et est
supprimé.
- Style de l'onglet actif simplifié : seul le border-bottom change de
couleur, le texte reste neutre (plus de changement de couleur du
libellé).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.docSidebarTabPanel { display:flex; } (règle auteur) gagnait
systématiquement sur le display:none natif de l'attribut [hidden]
(règle du navigateur) — l'origine "auteur" l'emporte toujours sur
l'origine "navigateur" en cascade CSS, peu importe l'ordre des
règles ou leur spécificité. forgeDocSwitchSidebarTab posait bien
l'attribut hidden (vérifié par un test jsdom qui, lui, ne teste que
la propriété DOM .hidden — angle mort qui a laissé passer ce bug),
mais son effet visuel était annulé : les deux onglets ("Pages" et
"Mise en page") s'affichaient empilés en permanence. Ajoute
.docSidebarTabPanel[hidden] { display:none; } pour reprendre la main.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace le carrousel une-page-à-la-fois par un panneau dédié plein
hauteur : liste verticale scrollable de toutes les pages, réordonnage
par glisser OU boutons haut/bas (accessibilité clavier), renommer
(crayon, édition en ligne), supprimer (protégé contre la suppression
de la dernière page), ajouter. La bibliothèque d'éléments passe dans
un second onglet "Mise en page", contenu inchangé. "Mise en page"
actif par défaut au chargement.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.docPageListItem/.docPageListSquare passent en width:100% (le carré
suit via aspect-ratio:1) au lieu d'une taille fixe étroite ; les
flèches ‹/› de navigation s'étirent en hauteur (align-items:stretch)
pour accompagner la carte désormais plus haute.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace le bandeau scrollable multi-cartes par un vrai carrousel
une-page-à-la-fois : un seul carré affiché (la page active), les
grandes flèches ‹/› aux extrémités changent directement la page
affichée (au lieu de juste faire défiler la vue). Les petites flèches
←/→ au-dessus du carré restent pour réordonner la page active parmi
ses sœurs, sans changer l'affichage — distinctes des flèches de
navigation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Chaque carte devient carré (numéro) + nom tronqué SOUS le carré, avec
les actions (réordonner/renommer/supprimer) au-dessus plutôt que sur
le côté. Ajoute deux flèches aux extrémités de la bande pour faire
défiler la VUE (distinct du réordonnancement par carte). Un titre
tronqué défile au survol de la souris (marquee), mesuré dynamiquement
— jamais pour un titre qui tient déjà.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Formes libres (rectangle/cercle/triangle/trait) retirées de bout en
bout (bibliothèque, rendu, panneau Propriétés, grille d'accroche,
JS/CSS associés) — fonctionnalité non retenue.
La bande de vignettes visuelles des pages au-dessus du canevas est
remplacée par une section "Pages" dans le panneau de gauche (liste
simple : ajouter/renommer/réordonner (haut/bas)/supprimer), à la
place de l'ex-catégorie "Mise en page" de la bibliothèque. La route
document_edit ne rend plus qu'une seule page (celle affichée) au
chargement, au lieu de toutes les pages pour alimenter les anciennes
vignettes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Les vignettes réutilisent le HTML réellement rendu de chaque page
(CSS scale trick) et sont centrées dans le conteneur du milieu, au
lieu d'une barre pleine largeur.
Corrige au passage deux bugs réels trouvés en écrivant les tests :
- une page contenant un mini-jeu (bouton Suivant/Recommencer) cassait
le parsing HTML car .docPageThumbCard était un <button> englobant
un autre <button> ; passage en div role="button" + équivalent
clavier, contenu copié rendu inert.
- le renommage d'une page par double-clic ne fonctionnait plus du
tout (sélecteur .docPageTab oublié lors du renommage des classes).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur : "il faut implementer un systeme de page". Un
support est desormais compose de PLUSIEURS pages (_document_pages),
chacune un document independant affiche seul sur le canevas -- chaque
element appartient a exactement une page via page_id (document_engine/
elements/*, routes/document/document_element_add.py/document_render.py
revalident desormais un page_id explicite). Migration automatique et
silencieuse pour les supports crees avant cette fonctionnalite
(db/supports/ensure_document_pages_schema.py, meme convention que les
ensure_X_schema.py existants) : leurs elements deviennent tous les
enfants d'une "Page 1" creee a la volee, aucune perte de contenu.
Nouveau paquet document_engine/pages/ (add/list/get/rename/move/delete)
et 4 routes dediees (routes/document/document_page_*.py) -- supprimer
la DERNIERE page restante est refuse (garde-fou pose a la route, meme
decoupage que routes/game/screens/screen_delete.py cote jeu, jamais
dans la fonction bas niveau).
Cote editeur : une bande d'ONGLETS au-dessus du canevas (jamais un
panneau lateral, choix explicite de l'utilisateur) -- clic pour changer
de page, double-clic pour renommer (contenteditable), glisser pour
reordonner, "+" pour ajouter, "x" pour supprimer. Changer de page vide
la pile Annuler/Retablir (une commande empilee sur une autre page n'a
plus de sens). Mode Apercu : navigation Page precedente/suivante avec
indicateur "Page X / N" (choix explicite : page par page, pas de
defilement continu), jamais affichee s'il n'y a qu'une seule page.
Verifie : suite pytest complete (702 tests, dont 14 nouveaux pour les
routes de pages), simulation DOM reelle (jsdom, 25 assertions couvrant
tout le cycle de vie cote client -- creation/bascule/renommage/
reordonnancement/suppression de page, portee correcte des elements par
page, pile Annuler/Retablir videe au changement de page, pilule de
navigation en Apercu), et un test de fumee HTTP reel contre le serveur
de dev en marche (creation/ajout d'element/rendu/renommage/suppression
d'une page, refus de supprimer la derniere page, page inconnue -> 404).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur : "la modale est transparente". Cause : les tokens
--doc-* (couleurs de fond/bordure/texte de l'editeur) sont definis
uniquement sur .docEditor3 ; #docScenarioTreeModal est volontairement
un FRERE de .docEditor3 dans le HTML (jamais un descendant, sinon
position:fixed serait rogne par le overflow:hidden de main.content --
meme bug deja trouve le 20/09/2026 pour le bandeau d'outils), donc ne
les heritait jamais -- var(--doc-bg-2) etc. retombaient sur transparent
partout dans la modale (fond du dialogue, mais aussi toutes les
couleurs d'accent/bordures/succes du graphe visuel).
Duplique la definition des tokens --doc-* sur #docScenarioTreeModal
(meme valeurs, meme variante [data-theme="light"]) plutot que de
deplacer la modale dans le DOM. forgeDocApplyTheme pose desormais le
meme data-theme sur les deux elements pour qu'ils restent synchronises.
Verifie via simulation DOM reelle (jsdom) : les deux elements recoivent
bien le meme data-theme apres un changement de theme.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur : "passons a un veritable graphe visuel". Chaque
nœud gagne une position x/y (document_engine/labels/scenario_config.py
: un x/y manquant/invalide retombe sur un quadrillage en cascade derive
de l'index du nœud, jamais (0, 0) pour tous les nœuds qui les
empilerait au meme endroit).
Cote editeur (static/document/js/document-editor.js) : les nœuds sont
des cartes deplacables a la souris sur un canevas (glisser-deposer reel,
meme principe que le glisser des formes libres), les choix relies a une
cible sont dessines comme des fleches SVG etiquetees par leur texte
(jamais un menu deroulant). Editer le texte/les choix d'un nœud se fait
dans un panneau inspecteur (colonne de droite) pour le nœud
selectionne ; relier un choix se fait en cliquant "Relier" puis le nœud
cible sur le graphe (mode connexion, Echap annule sans fermer la
modale). La modale generique (.docModal*) est agrandie specifiquement
pour ce graphe (jusqu'a 1180px) sans toucher sa taille par defaut.
Verifie via simulation DOM reelle (jsdom) : rendu des nœuds/positions,
glisser-deposer avec persistance au relachement, traces des fleches SVG
+ etiquettes, workflow complet du mode connexion, suppression d'un nœud
avec reparation des references pendantes, et les 3 façons de fermer la
modale (bouton/fond/Echap) y compris l'annulation du mode connexion
sans fermer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur : "une consequence peut mener a d'autres choix et
ainsi de suite, il n'y a pas de notion vrai/faux, il faut une modale
avec la possibilite de construire un veritable arbre de choix
consequence". Remplace le modele plat (situation + 2-4 choix + un seul
bon choix + une consequence terminale) par un vrai graphe de nœuds :
{"title", "nodes": [{"id", "text", "choices": [{"text", "target_id"}]}]}
- nodes[0] est la situation initiale, chaque choix peut pointer vers
n'importe quel autre nœud (branchement, convergence, fins multiples),
un nœud sans choix est une fin de branche valide. Aucune notion de
bonne/mauvaise reponse.
L'arbre se construit desormais dans une modale dediee (trop de
structure pour la colonne etroite du panneau Proprietes) : liste de
nœuds, chaque choix avec un menu deroulant "mene a" listant les autres
nœuds ou "fin de branche". La modale est un composant generique
(.docModal*) independant de tout framework externe.
sanitize_scenario_config degrade silencieusement tout target_id
orphelin (nœud supprime) vers None plutot que de faire echouer le
scenario entier. Le lecteur cote client navigue le graphe nœud par
nœud, le texte du nœud visite remplace le precedent (toujours pas un
Quiz), jusqu'a une fin de branche puis passage au scenario suivant.
Verifie via simulation DOM reelle (jsdom) : navigation ramifiee
(branchement, convergence, fin via nœud vide ET via choix sans cible),
plusieurs arbres a la suite, et l'editeur modal complet (ouverture,
ajout/suppression de nœud avec reparation des references pendantes,
changement de cible, fermeture bouton/fond/Echap).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cinquieme mini-jeu du support de formation : le createur ecrit une
situation initiale, definit 2 a 4 choix, indique lequel est le bon, et
redige la consequence de chaque choix. Plusieurs scenarios peuvent etre
crees, joues dans l'ordre d'ecriture (jamais melanges, contrairement a
Association/Memory/Mots meles : ce sont des mises en situation
sequentielles). En Apercu, l'apprenant lit la situation, choisit une
option, decouvre la consequence de SON choix et si c'etait le bon, puis
passe au scenario suivant. Comme Association/Memory/Mots meles, le
dernier scenario reste affiche une fois repondu : seul le bouton
Recommencer apparait, jamais un ecran de resultat separe (reserve au
Quiz). Reutilise les classes visuelles du Quiz (docQuizOptions/
docQuizFeedback/docQuizNextBar) plutot que de dupliquer ces regles.
Verifie via simulation DOM reelle (jsdom) : progression entre plusieurs
scenarios, choix correct/incorrect avec revelation de la bonne reponse,
comportement de fin de partie, panneau Proprietes (ajout/suppression de
scenario, changement du nombre de choix, selection du bon choix).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Quatrieme mini-jeu du support de formation : le createur ecrit 5 a 10
mots (recommandation souple, comme MIN_PAIRS/MIN_CARDS), places par le
serveur dans une grille carree horizontalement, verticalement, ou en
diagonale (deux sens seulement, jamais a l'envers) via un vrai
algorithme de placement avec retry/agrandissement de grille en cas de
conflit. La grille ET la position exacte de chaque mot sont calculees
cote serveur puis embarquees en JSON ; le client valide chaque
selection (glisser ou cliquer-cliquer) par comparaison de coordonnees
exactes, jamais une simple comparaison de texte (qui se tromperait sur
des lettres partagees entre deux mots qui se croisent). Comme
Association/Memory, la grille reste affichee une fois tous les mots
trouves : seul le bouton Recommencer (.docMinigameRestartBar, partage)
apparait.
Verifie via simulation DOM reelle (jsdom) : selection au glisser ET au
clic-clic, mot invalide sans crash, barre de fin qui bascule, panneau
Proprietes (ajout/suppression de mot avec revalidation serveur).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Les deux mini-jeux gardaient jusqu'ici un ecran de resultat separe qui
remplacait le plateau de jeu (comme le Quiz). Le plateau reste desormais
visible en permanence ; seule une barre partagee .docMinigameRestartBar
apparait/disparait. Le Quiz garde son propre comportement (ecran de
resultat separe), inchange.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur direct. La classe is-flipped n'etait deja jamais
retiree pour une carte appariee (forgeDocMemoryFlip) - elle restait
donc techniquement retournee - mais l'opacite reduite (55%) posee sur
.docMemoryCard.is-matched, a cote de cartes face cachee pleinement
opaques, se lisait visuellement comme "repartie face cachee". Retire
cette opacite et renforce l'etat "trouve" avec un fond/une bordure
verts sur la seule face visible (le verso), sans jamais assombrir le
contenu revele.
ruff/mypy --strict/stylelint tous verts (changement CSS pur, aucune
logique touchee) ; 62 tests document verifies frais.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
document_engine/labels/memory_config.py (nouveau) : modele de donnees,
meme convention resolve_X/sanitize_X que quiz_config.py/
association_config.py - DEFAULT_MEMORY_CONFIG, sanitize_memory_config.
Chaque carte a un recto ET un verso, chacun avec image/texte
independants et tous deux optionnels ; seul le verso doit avoir au
moins l'un des deux non vide (rien a reveler/apparier sinon) - le
recto peut rester entierement vide (dos de carte generique "?" par
defaut). Liste tronquee a MAX_CARDS=8.
Cote serveur, routes/document/document_element_update.py revalide
desormais aussi memory avant persistance. Le rendu (_render_memory)
affiche un resume reel (nombre de cartes, mode) et, des qu'au moins
une carte existe, un plateau de retournement REELEMENT interactif en
Mode Apercu (_render_memory_player) : en mode "paire", chaque carte
definie est DUPLIQUEE en deux instances partageant le meme card_index
(appariement classique) ; en mode "single", une seule instance par
carte (simple retournement, sans appariement - "c'est donc un
retourner de carte classique plus un jeu memory"). Les instances sont
melangees (random.shuffle, documente dans CODE_QUALITY.md) puis
embarquees en JSON dans data-memory-config.
Cote editeur, le panneau Proprietes propose un bascule segmentee
Paire/Simple et une liste de cartes repetable, chaque carte avec ses
deux faces (recto/verso) editables independamment (image + texte).
Extrait au passage forgeDocEscapeHtml (ex-forgeDocEscapeForTextarea,
generalise pour couvrir aussi les attributs) reutilise pour les deux
mini-jeux. Le plateau jouable (static/document/js/document-editor.js)
est une vraie carte-retournement CSS 3D (perspective/rotateY), contenu
de chaque face construit via DOM (textContent/img.src, jamais
innerHTML avec le texte du createur - meme precaution que le plateau
Association) : bon appariement verrouille en vert, mauvais reinitialise
apres un delai, ecran de resultat une fois le jeu termine (les deux
modes), et un "Recommencer" qui remelange reellement les cartes
(Fisher-Yates cote client).
Tests : 11 tests purs (tests/document/test_memory_config.py, sans
Flask, dont un qui verifie explicitement la duplication en mode paire
vs son absence en mode single) + 1 test de route verifiant la
sanitization a l'ecriture.
SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint tous
verts ; 62 tests document verifies frais. Verification manuelle live
complete : ajout, sanitization sur carte invalide, rendu du plateau,
et simulation DOM du gameplay reel dans les DEUX modes (mode paire :
mauvais appariement puis bon appariement puis jeu complet ; mode
single : retournement puis jeu complet) - script de diagnostic non
conserve dans le depot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retour utilisateur direct : les deux champs d'une paire etaient cote a
cote (trop etroit dans le panneau Proprietes) et en <input> simple.
Refonte de forgeDocRenderAssociationPairHtml en carte verticale
(meme habillage que .docQuizQuestion : bordure/fond/en-tete avec le
bouton supprimer) - "Element" et "Correspondance" sont desormais des
<textarea redimensionnables (rows=2, resize:vertical), separes par un
glyphe "up-down" plutot que le "left-right" horizontal precedent.
Ajoute au passage forgeDocEscapeForTextarea : le texte d'une paire est
insere comme CONTENU d'un <textarea> dans un template string (pas via
.value) - sans echappement, un texte contenant litteralement
"</textarea>" romprait le tag dans la propre session d'edition du
createur. Remarque a part (pas corrigee ici, hors demande) : le texte
d'une question de quiz (forgeDocRenderQuizQuestionHtml) a le meme
motif sans cet echappement - a signaler si souhaite comme correctif
separe.
SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/eslint/stylelint tous verts ; 50 tests document
verifies frais (aucune logique serveur touchee par ce changement
purement CSS/JS).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
document_engine/labels/association_config.py (nouveau) : modele de
donnees, meme convention resolve_X/sanitize_X que quiz_config.py -
DEFAULT_ASSOCIATION_CONFIG, sanitize_association_config (chaque paire
doit avoir ses deux cotes non vides, sinon supprimee silencieusement ;
liste tronquee a MAX_PAIRS=8).
Cote serveur, routes/document/document_element_update.py revalide
desormais aussi l'association avant persistance (meme raisonnement que
pour le quiz). Le rendu (_render_association) affiche un resume reel
(nombre de paires) et, des qu'au moins une paire existe, un plateau de
glisser-deposer REELEMENT interactif en Mode Apercu
(_render_association_player) : les deux colonnes (termes/
correspondances) sont melangees independamment (random.shuffle,
melange d'affichage documente dans CODE_QUALITY.md) puis embarquees en
JSON dans un attribut data-assoc-config.
Cote editeur, le panneau Proprietes d'une association ("relier
visuellement deux champs qui vont ensemble") est une liste de paires
repetable, chaque ligne reliant visuellement un champ "Element" et un
champ "Correspondance" par un glyphe ↔. Le plateau jouable en Apercu
(static/document/js/document-editor.js) supporte deux facons de jouer,
toutes deux reelles : glisser-deposer HTML5 natif, ou cliquer une
carte puis son emplacement (repli pour les appareils sans support
fiable du drag) - bonne association verrouillee en vert, mauvaise
signalee puis reinitialisee, ecran de resultat une fois toutes les
paires associees.
Bug reel trouve ET corrige via simulation DOM complete (glisser-
deposer + clic simules, pas juste un chargement de page) : le
feedback visuel reutilisait la classe CSS du quiz via une
reaffectation de className qui supprimait au passage la classe
d'identite docAssocFeedback, rendant l'element introuvable des le
premier essai de match (aurait plante en usage reel des la premiere
tentative). Corrige en gardant toujours les deux classes ensemble.
Tests : 8 tests purs (tests/document/test_association_config.py, sans
Flask) + 1 test de route verifiant la sanitization a l'ecriture.
SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint tous
verts ; 50 tests document verifies frais. Verification manuelle live
complete : ajout, sanitization sur paire invalide, rendu du plateau,
et simulation DOM du gameplay reel (glisser-deposer correct/incorrect,
clic-selection, progression, ecran de resultat) - script de
diagnostic non conserve dans le depot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le quiz affichait jusqu'ici la meme carte resume en edition comme en
Apercu. Ajoute un vrai questionnaire jouable, visible UNIQUEMENT en
Mode Apercu (masque en edition via CSS, jamais les deux a la fois) :
options de reponse cliquables, marquage correct/incorrect immediat,
score qui accumule les points de chaque question repondue
correctement, progression "Question suivante", et un ecran de
resultat final (score obtenu / total des points possibles) avec
"Recommencer le quiz" - structure et identite visuelle calquees sur
docs/plan/maquettes/document-formation-web.html (cartes en degrade,
options A/B/C/D, feedback vert/orange), adaptees aux tokens --doc-*
deja en place (theme clair/sombre inclus, nouvelles variables
--doc-quiz-success-*/--doc-quiz-danger-*).
Cote rendu (document_engine/rendering/render_document_element.py),
_render_quiz ajoute desormais le questionnaire (fonction privee
_render_quiz_player) des qu'au moins une question existe : les
questions sont embarquees en JSON dans un attribut data-quiz-config
(jamais un <script> par element), echappees pour l'HTML - aucun
aller-retour serveur pendant qu'on joue, tout le deroulé (reponse/
score/suivant/resultat) est gere par
static/document/js/document-editor.js. pointer-events, desactive sur
tout element en Apercu pour empecher la selection/le glisser-deposer
pendant le test, est reactive specifiquement dans le questionnaire
pour qu'il reste reellement cliquable.
Tests : 3 nouveaux tests de rendu purs (sans Flask, dont un qui
verifie l'echappement HTML du texte d'une question contre une
injection). Verifie en live via une simulation DOM complete
(basculer Apercu, repondre correctement puis incorrectement,
verifier score/feedback/etat des options, passer a la question
suivante, voir l'ecran de resultat) - le script de diagnostic n'a pas
ete conserve dans le depot.
SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint tous
verts ; 48 tests cibles (document + onboarding) verifies frais.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
document_engine/labels/quiz_config.py (nouveau) : modele de donnees
complet, meme convention resolve_X/sanitize_X que
game_engine/rendering/quiz_box_config.py cote jeu (aucun import
croise) - DEFAULT_QUIZ_CONFIG, sanitize_quiz_config (valide/nettoie
chaque question independamment, jamais ne leve, tronque a 2-4 choix,
remet correct_index a 0 si hors bornes/non-entier, clampe points >= 0
et timer_seconds dans [5,300]), quiz_total_points.
Cote serveur, routes/document/document_element_update.py revalide
desormais un quiz avant persistance (seul kind qui en a besoin - les
autres n'ont que des attributs scalaires sans structure a garantir) et
renvoie les attributs REELLEMENT persistes dans sa reponse, pour que
le client ne derive jamais de la verite serveur apres un nettoyage
serveur (ex. choix en trop tronque).
Cote editeur (static/document/js/document-editor.js), le panneau
Proprietes d'un quiz est desormais reel : chronometre optionnel,
couleur de theme, liste de questions repetable (ajout/suppression),
chacune avec son texte, un nombre de choix ajustable (2-4, les
inputs texte suivent), le choix correct via un radio par question,
et les points gagnes. Le rendu canevas (render_document_element.py)
affiche un resume reel (nombre de questions, total des points,
minuteur) au lieu du placeholder generique.
Tests : document_engine/labels/quiz_config.py couvert par 13 tests
purs (tests/document/test_quiz_config.py, sans Flask - defauts,
troncature, validation, cas limites dont bool comme correct_index) +
1 test de route verifiant la sanitization a l'ecriture et le rendu.
SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint tous
verts ; 63 tests cibles (document + onboarding + auth) verifies
fraichement + verification manuelle live via le serveur de dev
(ajout, sanitization sur choix invalides/en trop, rendu du resume
avec minuteur).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bug reel (retour utilisateur direct, capture d'ecran a l'appui) :
.docEditor3 utilisait position:fixed; inset:0 pour occuper tout
l'ecran, mais son ancetre main.content a overflow:hidden (voir
body.objectEditBody dans static/style.css) — qui ROGNE VISUELLEMENT
tout descendant position:fixed a sa propre boite, laquelle commence
sous la barre de navigation du site, pas au vrai sommet du viewport.
Le bandeau d'outils (56px) tombait entierement dans la zone rognee,
invisible, pendant que le reste de l'editeur semblait juste decale
vers le haut a sa place. Corrige en respectant le pattern deja etabli
par les autres editeurs (body.objectEditBody + main.content en
flex:1 1 auto) plutot qu'un overlay fixed — .docEditor3 est desormais
un simple enfant flex qui remplit main.content. Meme correction pour
le tiroir mobile des barres laterales (position:absolute relatif a
.docBodyWrap plutot que position:fixed relatif au viewport).
Ajoute aussi une regle pour que l'editeur occupe toute la largeur de
la fenetre bord a bord (demande explicite) : .content/.content-wide
imposent normalement 760px/1600px avec padding fixe.
SKIP=djlint : meme backlog H021 pre-existant que les commits
precedents (document_edit.html verifie clean individuellement).
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint
tous verts ; 24 tests du support de formation verifies apres coup
(changement CSS/Jinja pur, aucune logique serveur touchee) ; verifie
en live que le serveur sert bien le CSS corrige.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Un apprenant n'a jamais acces a l'editeur : un etat "publie" persiste
en base (published_at) ne servait donc a rien tant qu'aucune vue
apprenant/export ne le consomme (retour utilisateur direct). Retrait
complet : route /document/<slug>/publish, db.mark_support_published,
la colonne logique published_at dans support_meta, le bouton et son
CSS/JS. "Aperçu" devient l'action primaire du bandeau (repond au vrai
besoin : voir le rendu avant un futur export). Un export reel (SCORM
ou equivalent) reste a specifier separement le jour venu.
SKIP=djlint : meme backlog H021 pre-existant que le commit precedent,
aucun fichier touche ici n'y figure. ruff/mypy --strict/vulture/
bandit/import-linter/eslint/stylelint tous verts ; 615 tests Python
(617 - 2 tests du Publier retire) + verification manuelle live du
retrait (route 404, bouton absent du HTML, Apercu confirme
fonctionnel par simulation DOM).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Nouveau moteur document_engine/ (elements CRUD + rendering + labels),
db/supports/ (stockage independant de db/games), routes/document/
(CRUD AJAX + publication), et l'editeur frontend complet
(templates/document/, static/document/) avec moteur de layout reel
(glisser-deposer -> fusion en rangee ou insertion avant/apres), vrai
Undo/Redo par pile de commandes, grille d'accroche pour les formes
libres, apercu responsive a largeurs fixes, mode Apercu, et publication
persistee.
"Mes formations" (templates/index.html) liste desormais les
environnements 2D et les supports de formation cote a cote ;
l'onboarding et core/auth_guard.py sont generalises pour qu'un compte
restreint puisse posseder un projet de chaque type independamment.
SKIP=djlint : le hook ne signale que le backlog H021 (styles en ligne)
deja documente dans CODE_QUALITY.md sur des fichiers pre-existants non
touches ici (base.html, game/play.html, scene_edit.html,
game_dashboard_simple.html, clause_row.html) plus une ligne de
index.html deja presente avant cette session — aucun nouveau fichier
(document_edit.html compris) n'y figure. Tous les autres outils
(ruff, mypy --strict, vulture, bandit, import-linter, eslint,
stylelint) passent sans erreur ; 617 tests Python + 276 tests JS
verts, plus une verification manuelle complete du cycle de vie via le
serveur de developpement.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>