Commit Graph
232 Commits
Author SHA1 Message Date
williamandClaude Sonnet 5 c04a81cfec La consequence du Scenario remplace la situation, pas un feedback Quiz
Build and deploy / test-python (push) Failing after 1m2s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / lint-python (push) Failing after 58s
Build and deploy / lint-js (push) Failing after 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m1s
Retour utilisateur : "ce n'est pas un quiz, la consequence s'affiche a
la place de la situation precedente". L'ancien design affichait la
situation ET les choix en permanence avec un encart de feedback separe
en dessous (calque sur le Quiz). Desormais un seul bloc de texte
(.docScenarioSituation) sert successivement a la situation PUIS, une
fois un choix fait, a la consequence a sa place ; les boutons de choix
disparaissent avec elle. Supprime l'element .docScenarioConsequence
devenu inutile.

Verifie via simulation DOM reelle (jsdom) : la consequence remplace bien
le texte de la situation (jamais affichee a cote), les choix
disparaissent, le retour a une situation neutre au scenario suivant/au
redemarrage fonctionne.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:10:56 +02:00
williamandClaude Sonnet 5 b2292de122 Implemente le mini-jeu Scenario (situation, choix, consequences)
Build and deploy / test-python (push) Failing after 59s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / lint-python (push) Failing after 59s
Build and deploy / lint-js (push) Failing after 1m1s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 59s
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>
2026-09-20 16:50:10 +02:00
williamandClaude Sonnet 5 d8be80ebd1 Implemente le mini-jeu Mots meles (grille reelle, placement 4 directions)
Build and deploy / test-python (push) Failing after 1m0s
Build and deploy / test-js (push) Failing after 50s
Build and deploy / lint-python (push) Failing after 1m2s
Build and deploy / lint-js (push) Failing after 1m7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m1s
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>
2026-09-20 15:31:46 +02:00
williamandClaude Sonnet 5 1b1307bb85 Association et Memory restent affiches une fois termines, seul Recommencer bascule
Build and deploy / test-python (push) Failing after 59s
Build and deploy / test-js (push) Failing after 52s
Build and deploy / lint-python (push) Failing after 59s
Build and deploy / lint-js (push) Failing after 1m1s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m4s
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>
2026-09-20 14:43:26 +02:00
williamandClaude Sonnet 5 a34a95a3b4 Une paire de Memory trouvee reste clairement face retournee
Build and deploy / test-python (push) Failing after 51s
Build and deploy / test-js (push) Successful in 48s
Build and deploy / lint-python (push) Failing after 1m3s
Build and deploy / lint-js (push) Failing after 1m2s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m2s
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>
2026-09-20 14:23:38 +02:00
williamandClaude Sonnet 5 4111081e1a Implemente le mini-jeu Memory (retournement de cartes, mode paire/single)
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>
2026-09-20 14:13:28 +02:00
williamandClaude Sonnet 5 cf46da3388 Formulaire de paire (Association) en pile verticale + textarea
Build and deploy / test-python (push) Failing after 55s
Build and deploy / test-js (push) Successful in 49s
Build and deploy / lint-python (push) Failing after 1m2s
Build and deploy / lint-js (push) Failing after 1m0s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m0s
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>
2026-09-20 13:19:36 +02:00
williamandClaude Sonnet 5 081b65f48f Corrige "+ Ajouter une paire" qui ne faisait rien (Association)
Build and deploy / test-python (push) Failing after 1m5s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / lint-python (push) Failing after 1m5s
Build and deploy / lint-js (push) Failing after 1m6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m0s
Retour utilisateur direct : cliquer "+ Ajouter une paire" ne montrait
aucune nouvelle ligne. Cause reelle : la nouvelle paire etait creee
avec ses deux champs vides ({left:'', right:''}), or
sanitize_association_config (routes/document/document_element_update.py)
rejette a raison toute paire dont un cote est vide - la paire
disparaissait donc silencieusement des sa creation, sans aucun signal.
Corrige en pre-remplissant des valeurs par defaut non vides ("Nouvel
element" / "Sa correspondance"), meme strategie que
forgeDocQuizNewQuestion (deja correcte pour le quiz).

Verifie via sanitize_association_config directement (la paire par
defaut survit desormais) et via un appel HTTP complet reproduisant
exactement l'action du bouton cote client (element cree, persiste,
rendu dans le badge "1 paire").

SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict tous verts ; 50 tests document verifies frais.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 13:07:54 +02:00
williamandClaude Sonnet 5 ce750ec697 Implemente le mini-jeu Association (glisser-deposer par paires)
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>
2026-09-20 13:01:21 +02:00
williamandClaude Sonnet 5 9fb22be1ae Quiz reellement interactif en Mode Apercu (voir maquette document-formation-web.html)
Build and deploy / test-python (push) Failing after 1m5s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / lint-python (push) Failing after 1m2s
Build and deploy / lint-js (push) Failing after 1m4s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m0s
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>
2026-09-20 12:08:03 +02:00
williamandClaude Sonnet 5 9d87a4bfef Implemente le mini-jeu Quiz (questions/choix/points/timer)
Build and deploy / test-python (push) Failing after 1m3s
Build and deploy / test-js (push) Successful in 48s
Build and deploy / lint-python (push) Failing after 1m2s
Build and deploy / lint-js (push) Failing after 1m5s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m5s
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>
2026-09-20 11:40:48 +02:00
williamandClaude Sonnet 5 597d99a8de Corrige le bandeau d'outils invisible et force l'editeur en pleine largeur
Build and deploy / test-python (push) Successful in 6m33s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / lint-python (push) Failing after 4m13s
Build and deploy / lint-js (push) Failing after 1m10s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m5s
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>
2026-09-20 10:37:29 +02:00
williamandClaude Sonnet 5 7ca03380b7 Retire l'action Publier du support de formation
Build and deploy / test-python (push) Successful in 6m57s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / lint-python (push) Successful in 4m14s
Build and deploy / lint-js (push) Failing after 1m12s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 3m48s
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>
2026-09-20 09:58:53 +02:00
williamandClaude Sonnet 5 bab581737a Ajoute le support de formation : entite racine separee du jeu 2D
Build and deploy / test-python (push) Successful in 11m2s
Build and deploy / test-js (push) Successful in 1m2s
Build and deploy / lint-python (push) Successful in 4m6s
Build and deploy / lint-js (push) Failing after 1m21s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 4m2s
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>
2026-09-20 09:20:33 +02:00
williamandClaude Sonnet 5 7e4a761cbb Met à jour docs/plan/PLAN.md section 0 avec la restructuration réellement faite
Build and deploy / test-python (push) Successful in 5m38s
Build and deploy / test-js (push) Successful in 43s
Build and deploy / lint-python (push) Successful in 4m34s
Build and deploy / lint-js (push) Successful in 2m52s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 3m46s
La section 0 décrivait encore un simple renommage screens->game_engine,
dépassé le même jour par la réorganisation effective en sous-dossiers
game/document dans routes/, scripts/, static/, templates/, tests/ (voir
commit b2e933f3). Réécrite pour refléter l'état réel : contenu exact
déplacé dans chaque game/, styles/ volontairement intact, ai/publish/
non déplacés, les 2 fichiers orphelins signalés. Étape 1 de la section 5
marquée faite. Aucun changement sur le contenu fonctionnel des sections
1/3/4 (travail futur, non concerné par cette réorganisation).

SKIP=djlint : aucun template touché par ce commit, backlog H021 deja
documente comme dette assumee (CODE_QUALITY.md section 6).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 12:44:09 +02:00
williamandClaude Sonnet 5 b2e933f322 Reorganisation game/document : renommage screens->game_engine + sous-dossiers game/ dans routes, scripts, static, templates, tests
Build and deploy / test-python (push) Successful in 11m12s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / lint-python (push) Successful in 3m56s
Build and deploy / lint-js (push) Successful in 3m1s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 3m58s
Prepare la scission a venir entre l'editeur Jeu 2D et le futur editeur
Support de formation (voir docs/plan/PLAN.md), sans toucher a
l'architecture en couches existante :

- screens/ renomme en game_engine/ (nom clair pour le moteur du jeu 2D,
  avant l'arrivee d'un second "moteur" cote document) : ~85 imports
  corriges, contrat import-linter mis a jour, meme forme de couches.
- routes/, scripts/, static/, templates/, tests/ : tout ce qui est
  propre au jeu 2D deplace dans un sous-dossier game/ de chacun
  (routes/game/, static/game/, templates/game/, tests/game/,
  scripts/game/) ; ce qui est partage par le site (auth, onboarding,
  dashboard, uploads, db/) reste a la racine de chaque dossier. Un
  sous-dossier document/ (vide) cree dans chacun pour le futur chantier.
- styles/ volontairement inchange : les 3 fichiers sources sont
  concatenes en un seul static/style.css charge par tout le site,
  scinder leur CONTENU (editeur vs partage) serait un refactor CSS
  distinct, pas un deplacement mecanique.
- Chaine d'export SCORM (publish/build_scorm_package.py) mise a jour en
  profondeur : copie des assets, URLs d'icones relatives a
  static/style.css (qui ne bouge pas), manifeste, wrapper SCORM.
- Deux regressions d'un sweep de renommage anterieur corrigees au passage
  (screens.js/screens/scene-objects incorrectement convertis en
  game_engine.js/game_engine/scene-objects dans des commentaires).
- Effet de bord Windows decouvert et corrige : git mv + Path.write_text
  convertissent des fichiers en CRLF (core.autocrlf=true) - ~189 fichiers
  normalises en LF.
- .eslintrc.json/package.json : uniquement les chemins de glob mis a jour
  (static/game/js/...) ; la preparation eslint-plugin-unicorn du lot 7
  reste volontairement non committee (package-lock.json restaure a la
  version precedente).

Verifications : ruff, mypy --strict (391 fichiers), vulture, bandit,
lint-imports tous verts ; 591/591 tests Python, 276/276 tests JS ;
demarrage serveur + requetes HTTP manuelles confirmant que les assets
deplaces repondent en 200 au nouvel emplacement et 404 a l'ancien.

SKIP=djlint : backlog H021 (styles inline) deja documente comme dette
assumee dans CODE_QUALITY.md section 6, aucun template touche par ce
commit au-dela d'un deplacement de fichier.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 12:27:53 +02:00
williamandClaude Sonnet 5 ccf836f2c5 Lot 4 modernisation JS (SonarLint) + restauration CI sonarqube non-bloquante
Build and deploy / test-python (push) Successful in 11m18s
Build and deploy / test-js (push) Successful in 1m16s
Build and deploy / lint-python (push) Successful in 4m24s
Build and deploy / lint-js (push) Successful in 2m51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Successful in 5m30s
Corrige les findings SonarQube (via SonarLint IDE, fichier par fichier)
sur ~24 fichiers static/js/ : parseFloat/parseInt -> Number.*,
.replace(/x/g,y) -> .replaceAll, .indexOf() -> .includes()/.startsWith(),
getAttribute/setAttribute -> .dataset, tableaux -> Set, x && x.y -> x?.y
(verifie site par site), extraction de template litteraux imbriques,
ternaires imbriquees, refactors de complexite cognitive (S3776) via
tables de dispatch, Object.hasOwn, .at(), et deduplication de fonctions
identiques (S4144). Deux exceptions S2486 documentees/corrigees
(filter-repeater-rows.js) et un cas S2703 de partage inter-scripts
complete (_collisionWizard, trigger-editor.js <-> collision-rules-editor.js).
Details complets dans CODE_QUALITY.md section 5.

Restaure aussi le job CI "sonarqube" (non-bloquant) dans
.gitea/workflows/deploy.yml maintenant que l'instance prod est
operationnelle.

Suites vertes : 276/276 JS (node --test), 591/591 Python (pytest).

SKIP=djlint sur ce commit : hook djlint bloquant sur le backlog H021
(styles inline, 49 occurrences/6 templates) deja documente comme dette
assumee non traitee dans CODE_QUALITY.md section 6, aucun rapport avec
ce commit (aucun template touche ici) - valide explicitement avec
l'utilisateur avant de contourner ce hook.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 16:38:29 +02:00
williamandClaude Sonnet 5 66d8eaeae8 Lots 1-3 modernisation JS (S8786/S2703/S2486) + retrait Sonar CI/prod
- Lot 1 (S8786, ReDoS) : 5 sites documentes NOSONAR apres preuve empirique
  (script reproductible docs/redos_probe_s8786.js), aucune reecriture
  defensive necessaire.
- Lot 2 (S2703, variable globale implicite) : bug reel trouve et corrige
  (SCENE_OBJECT_NAMES en const au lieu de let, cassait la reassignation
  cross-script depuis scene-editor.js) + test de non-regression ; 4 autres
  sites confirmes surs et documentes.
- Lot 3 (S2486, exceptions avalees) : 6 sites confirmes surs et
  documentes ; 2 sites (config sprite JSON invalide) corriges avec un
  console.warn devtools, comportement joueur inchange, couverts par un
  nouveau test.
- Retrait du job CI sonarqube (.gitea/workflows/deploy.yml) et du service
  prod sonarqube/sonar-postgres (docker-compose.prod.yml) : acces dashboard
  bloque par des soucis d'infrastructure reseau (WSL2/pare-feu Hyper-V en
  local, reseau Docker partage avec Caddy pas en place en prod), sans lien
  avec le code du moteur - mis de cote plutot que de continuer a bloquer
  sur de l'infra. Les lots 4+ de modernisation JS dependent de scores
  Sonar exacts et sont donc egalement en pause (voir CODE_QUALITY.md).

SKIP=djlint : H021 (styles inline, 49 occurrences) est un backlog deja
documente et assume (CODE_QUALITY.md section 6), sur des templates non
touches par ce commit - deja exclu de la CI pour la meme raison.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 08:46:24 +02:00
williamandClaude Sonnet 5 55e81c0fbb Ajoute CODE_QUALITY.md et corrige S7632 a la racine (helper assert_not_none)
- Nouveau db/assert_not_none.py : centralise l'unique suppression
  Bandit/Ruff (# nosec B101 / # noqa: S101) de narrowing de type dans
  tout le moteur. Remplace 11 sites disperses (ai/chat.py, ai/tools.py,
  routes/scenes/scene_object_{add,collision,geometry,personnage_data,
  quiz_config}.py, screens/payload/full_game_payload.py,
  scripts/build_demo_dialogues.py) qui repetaient chacun le meme
  commentaire empile - Sonar (python:S7632) ne parse pas deux
  commentaires # sur une ligne, meme si Ruff et Bandit les acceptent
  chacun tres bien (contrainte structurelle documentee dans
  CODE_QUALITY.md : chaque outil exige son propre mot-cle immediatement
  apres un #, aucun format a un seul # ne peut satisfaire les deux a la
  fois). Les 6 sites # nosec B608 (SQL dynamique) restent inchanges,
  nature differente, hors perimetre de ce refactor.
  Verifie : mypy --strict propre (389 fichiers), ruff/bandit/import-linter
  clean, suite complete verte (591 tests), scan SonarQube local relance
  confirmant S7632 a 7 (1 seul site restant dans le helper lui-meme + les
  6 B608), 0 bug (une regression S8371 trouvee et corrigee en route).

- 4 sites |safe repositionnes sur leur ligne exacte (scene_edit.html,
  register_2fa.html, onboarding_new.html, play.html) - le marqueur
  NOSONAR etait sur la ligne precedente par erreur (meme piege que celui
  documente pour S8371 ci-dessus). Confirme par scan que meme corrige,
  l'analyseur Web de Sonar ne supporte aucune syntaxe de suppression
  inline testee pour la regle Web:S5247 - documente comme limitation
  technique connue dans CODE_QUALITY.md plutot que force.

- CODE_QUALITY.md (nouveau) : reference complete du dispositif qualite -
  vue d'ensemble par outil, configuration de chacun, commandes de lancement
  local, procedure de justification d'une exception (avec le format exact
  attendu par Bandit/Ruff/Sonar, verifie empiriquement), table des 10
  exceptions documentees, backlog (djLint H021, code smells Sonar).

djLint (H021, styles inline) volontairement saute pour ce commit - meme
backlog assume que les commits precedents, aucun rapport avec ce changement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 12:41:02 +02:00
williamandClaude Sonnet 5 20f9398bd4 Phase 4 (CI) : securise le job deploy (token registre, cle SSH)
- docker login sur le serveur distant : passe le token via
  --password-stdin (echo | docker login ...) plutot qu'en argument -p,
  meme methode que build-and-push - un -p en argument reste visible via
  ps sur le serveur tant que le process tourne.
- Nouvelle etape "Nettoyage de la cle SSH" (if: always()) : supprime
  ~/.ssh/deploy_key en fin de job, meme si une etape precedente a
  echoue. rm -f (jamais rm nu) : sortie 0 que le fichier ou meme le
  dossier ~/.ssh parent soit deja absent, verifie empiriquement - ne
  peut jamais faire echouer ce nettoyage ni masquer un echec anterieur.
  Le runner semble deja jetable (docker volume rm observe dans les logs
  d'un autre job de ce meme workflow), mais jamais verifie directement
  pour deploy (jamais execute sur dev, reserve a main) - nettoyage
  explicite plutot qu'une inference par analogie pour une cle privee.

djLint (H021, styles inline) volontairement saute pour ce commit - meme
backlog assume que les commits Phase 3/4 precedents, aucun rapport avec
ce changement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 19:39:39 +02:00
williamandClaude Sonnet 5 2ff127f68e Phase 4 (CI) : lint-python/lint-js bloquants + sonarqube non-bloquant
Build and deploy / test-python (push) Successful in 13m44s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / lint-python (push) Successful in 4m32s
Build and deploy / lint-js (push) Successful in 3m5s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 4m3s
Les hooks pre-commit locaux (deja tous verts, voir le commit Phase 3) ne
protegent que la machine du committeur, jamais un push direct ou une PR
mergee depuis ailleurs sans passer par ces hooks. Ajoute donc en CI :

- lint-python (ruff check/format, mypy --strict, vulture, bandit,
  import-linter) et lint-js (eslint, stylelint) : bloquants des
  maintenant, memes commandes que .pre-commit-config.yaml, deja verts en
  local donc aucune raison d'attendre. djlint volontairement exclu (49
  H021 deja en backlog assume, a ajouter ici une fois ce lot traite).
  build-and-push en depend desormais (needs), en plus des tests deja en
  place.

- sonarqube : scan contre l'instance self-hebergee (sonar.forgebase.fr)
  a chaque push. continue-on-error: true au niveau du job (non-bloquant
  pendant cette premiere periode, le temps de trier le rapport deja
  analyse en Phase 3 - code smells/vulnerabilites), et absent du needs
  de build-and-push : un echec ici n'affecte jamais le reste du
  pipeline, meme si l'infra n'est pas encore prete (echec attendu tant
  que SONAR_TOKEN n'est pas configure cote Gitea).

Secret a ajouter dans Gitea (Parametres du depot > Actions > Secrets) :
SONAR_TOKEN (jeton d'analyse genere sur sonar.forgebase.fr, jamais le
mot de passe admin).

djLint (H021, styles inline) volontairement saute pour ce commit - meme
backlog assume que le commit Phase 3, aucun rapport avec ce changement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 16:26:21 +02:00
williamandClaude Sonnet 5 c57420c8c9 Phase 3 : hardening qualite de code - typage strict, securite, dead code, a11y
Config strictement stricte partout (ruff, mypy --strict, bandit, vulture,
import-linter, eslint, stylelint), aucune regle desactivee "pour ne pas
casser le build" - l'existant a ete corrige pour la satisfaire plutot que
l'inverse. Hooks pre-commit locaux (language: system) bloquants.

- Typage mypy --strict propage a tout le moteur (db, screens, auth, core,
  ai, routes, puis publish/scripts/tests/app.py/build_css.py).
- Securite : fuite de handle fichier Windows corrigee dans l'export SCORM
  (routes/publish/export_scorm.py), CSRF/RNG non-crypto/xAPI documentes
  (# nosec, # NOSONAR justifies), nouveau db.json_for_script() (echappe
  "</script>" dans le JSON embarque en <script>, 25 sites).
- Architecture : imports circulaires/F811 nettoyes, contrats
  import-linter respectes, code mort retire (vulture).
- Accessibilite : 69 champs de formulaire sans label correctement
  associe corriges (for/id ou aria-label) sur 11 templates.
- ESLint/Stylelint : lot mecanique JS/CSS, regles ajustees puis
  appliquees (aucune desactivee sans verification individuelle).
- Tests : isolation du compte admin partage (nettoyage ponctuel +
  fixture de teardown automatique en filet de securite), suite complete
  verte (591 tests Python, 241 tests JS).
- SonarQube Community Build self-heberge (Docker + PostgreSQL) : rapport
  complet analyse point par point, faux positifs documentes.
- .gitattributes ajoute (LF force) : core.autocrlf=true sur cette machine
  faisait echouer ESLint (linebreak-style) via un bug connu de git
  (checkout "en place" qui ignore l'eol force sur un fichier deja
  present sur disque - contourne en supprimant puis recreant chaque
  fichier suivi).

djLint (H021, styles inline) volontairement saute pour ce commit -
backlog assume, deja documente, traite dans un lot separe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 16:06:15 +02:00
williamandClaude Sonnet 5 7db4803b93 Ajoute la fonctionnalite quiz autonome/plein ecran a la boite a quiz
Build and deploy / test-python (push) Successful in 12m10s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Introduit la double categorie de modeles (boite de dialogue / page de
quiz plein ecran) avec plein ecran, minuteur, score integre et ecran de
resultat pour les modeles page ; ajoute les modeles "Manga" (boite et
page) et "Classique" (page), pilotables aussi par l'assistant IA Ruby.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 15:07:23 +02:00
williamandClaude Sonnet 5 559331f9cf Enrichit les declencheurs/actions de scene (clic/survol/affichage, surbrillance/video/son/visibilite/indication/attendre) et fiabilise la pose d'un fond/decor importe
Build and deploy / test-python (push) Successful in 10m59s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
- Ajoute clic/survol/affichage-ecran comme declencheurs, et surbrillance,
  video, son, visibilite, indication, attendre comme actions, utilisables
  aussi bien par l'editeur manuel (menu lateral Objets/Ecran) que par
  Ruby (IA), avec blocs deplacables/supprimables dans une chaine.
- Corrige plusieurs variantes du bug "impossible de poser un objet hors
  du champ de la camera" (troncature du chainage d'actions a 4 maillons,
  fond importe pose a 128x128 au lieu de sa taille reelle, decalage du
  fond au vrai glisser-depose, redimensionnement manuel jamais propage
  au monde).
- Ajoute un vrai glisser-depose depuis la galerie vers la scene, la
  gestion complete de "Mes assets" (sous-sections Fonds/Decors/Sons/
  Videos, suppression, reclassement fond<->decor sans re-upload).
- Ajoute l'upload de son (limite 3 min) et de video (MP4 uniquement,
  limite 5 min), avec validation de la duree reelle du fichier, et une
  replique audio optionnelle dans une bulle de dialogue.
- Fixe la taille de pose d'un objet/decor importe a 200x200 avec une
  boite de collision de 150x150.
- Filtre le selecteur de fichier des actions son/video par type reel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 00:28:46 +02:00
williamandClaude Sonnet 5 b07b231a61 Ajoute l'assistant IA "Ruby" (Claude + Scenario) et corrige plusieurs bugs de la scène
Build and deploy / test-python (push) Successful in 12m24s
Build and deploy / test-js (push) Successful in 1m9s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Intègre un chat IA capable de manipuler la scène via les mêmes fonctions
que l'éditeur manuel (objets, variables, déclencheurs, images générées),
avec conversations multiples par écran façon Claude. Corrige au passage
le rafraîchissement pjax hors-ordre, l'onglet IA/déclencheurs vide après
sélection d'un objet, la comparaison de booléens dans les conditions, et
le blocage du glisser-déposer hors du cadre caméra après un redimensionnement
de fond par l'IA.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 19:40:31 +02:00
williamandClaude Sonnet 5 ab6635eee0 Corrige l'action "Modifier une variable" sans effet en aperçu créateur
Build and deploy / test-python (push) Successful in 5m47s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
compute-operation.js/apply-actions.js n'étaient chargés que dans le
paquet SCORM exporté (offline_mode) ; en aperçu créateur (/game/<slug>/play),
forgeApplyVariableActionOffline était donc absente et l'action ne modifiait
rien. Charge les deux scripts inconditionnellement dans play.html.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 22:24:47 +02:00
williamandClaude Sonnet 5 d664ed5637 Remplace le système de quêtes par des déclencheurs, ajoute l'action variable et le chaînage
Build and deploy / test-python (push) Successful in 8m45s
Build and deploy / test-js (push) Successful in 49s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Supprime le concept de "quête" au profit d'un onglet unique "Déclencheurs"
portant toute la logique (dialogue, condition, marquage terminé) directement
sur l'objet de scène. Ajoute une nouvelle action "Modifier une variable"
(réutilisant le vocabulaire du graphe de flow) utilisable après une
collision, une interaction ou une branche de condition, ainsi qu'un
chaînage d'actions ("then") permettant d'enchaîner plusieurs actions à la
suite et d'étendre un déclencheur déjà posé sans le recréer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 22:05:06 +02:00
william c504ace167 Enrichit le reporting SCORM/xAPI et met en conformité RGAA le player
Build and deploy / test-python (push) Successful in 5m56s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
SCORM/xAPI :
- Ajoute l'export SCORM 2004 (3rd/4th edition) au choix, en plus du 1.2
  par défaut : sépare completion_status/success_status (un échec reste
  "completed" au lieu de retomber à tort en "incomplete" comme force la
  1.2), remonte aussi cmi.interactions.n.* par question répondue.
- Fournit cmi.core.score.min/max (1.2 et 2004), calculé depuis les
  récompenses de quiz du jeu, pour que le LMS affiche un vrai pourcentage
  au lieu du score brut à tort étiqueté "%".
- Libellés de verbes xAPI en français en plus de l'anglais.

Accessibilité (RGAA/WCAG 2.1 AA) sur le player :
- Navigation clavier des objets de scène "au clic"/"au survol"
  (tabindex, role, Entrée/Espace, focus/blur).
- alt sur les images (nom auteur ou décoratif), aria-hidden sur les
  icônes seules, role="dialog"/aria-live sur les boîtes de dialogue/quiz.
- Landmark <main> + titre de page, respect de prefers-reduced-motion.
- Le quiz n'avance plus automatiquement après un délai fixe : bouton
  "Continuer →" explicite (RGAA 2.2.1).
- Avertissement de contraste dans l'éditeur de style de dialogue.
- Déclaration d'accessibilité téléchargeable depuis la modale d'export.
2026-09-06 00:17:51 +02:00
william f7a50e5afe Ajoute un suivi xAPI optionnel (bolt-on) au paquet SCORM exporté
Build and deploy / test-python (push) Successful in 9m41s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Greffe l'envoi de statements xAPI vers un LRS configurable par le
créateur du jeu, en parallèle du reporting SCORM existant : réglages
stockés en base (_meta), formulaire dans la modale d'export, injection
dans le paquet exporté. Relié aussi bien à l'action de flow "Modifier
un score/statut" qu'au parcours quête/quiz (qui alimentait déjà le
SCORM classique via un chemin séparé).

L'export ne se lance plus automatiquement à l'ouverture de la modale,
pour laisser le temps d'enregistrer les réglages xAPI avant de générer
le paquet.
2026-09-05 10:00:57 +02:00
william 7ebc9b143f Corrige une faille d'isolation entre comptes et retire l'email des chemins de projet
Build and deploy / test-python (push) Successful in 9m39s
Build and deploy / test-js (push) Successful in 52s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Le rôle "admin" contournait entièrement l'isolation par projet
(core/auth_guard.py) : il pouvait ouvrir/modifier/supprimer le jeu de
n'importe quel autre compte en connaissant son slug, et la page d'accueil
listait sans filtrage tous les projets de tous les comptes.

- La propriété d'un projet se vérifie désormais sur le segment
  "propriétaire" du slug (id du compte), pour tous les rôles y compris
  admin — un slug "à plat" (sans compte associé) reste réservé à
  l'admin, comportement historique conservé pour ce cas précis.
- routes/games/index.py ne liste plus que les projets du compte connecté.
- Le dossier propriétaire d'un projet est maintenant l'id numérique du
  compte, plus jamais son email slugifié (visible en clair dans chaque
  URL auparavant) — script de migration fourni et déjà exécuté sur les
  données existantes.
- Changer d'email ne renomme plus aucun dossier (n'en dépend plus).
- Deux nouveaux tests de régression, fixtures corrigées en conséquence.
- README réécrit pour refléter l'état actuel du produit (jeu 2D
  uniquement, plus de traces de l'ancien éditeur "document").
2026-09-04 22:52:05 +02:00
william 50835a18e2 Supprime le type d'écran "document" et recentre le produit sur le jeu 2D
Build and deploy / test-python (push) Successful in 9m41s
Build and deploy / test-js (push) Successful in 1m0s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Le document de cadrage produit cible des formateurs non techniques créant
des serious games/quiz gamifiés — l'éditeur générique "document" (blocs de
logique en nœuds, timeline d'animation, définitions d'objets/relations,
templates réutilisables) est une complexité hors cible que l'effort
d'ingénierie récent avait déjà abandonnée au profit du jeu_2d.

- Onboarding : ne garde que le parcours "RPG" (jeu_2d), retire
  Quiz/Embranchement/Créer mon jeu de A à Z (tous document-only)
- Suppression en bloc des modules exclusifs au document : routes/elements,
  routes/element_types, routes/objects, routes/legacy_actions,
  screens/elements, screens/element_types, screens/widgets,
  screens/legacy_actions, le rendu render_element_html.py et son cluster,
  templates/screen_edit.html, templates/game_dashboard.html,
  flow-editor.js/tabs-and-blocks.js/animation-timeline.js
- Dashboard toujours simplifié (un seul mode possible désormais)
- Tests document-only supprimés, tests de logique partagée (flow,
  événements personnalisés, animations) retargetés sur des écrans jeu_2d
- Aucune régression jeu_2d : 299 tests passent

Carte d'onboarding retravaillée : argumentaire RH non technique (liste à
coche, badge "Compatible LMS"), taille et interaction de retournement
ajustées.
2026-09-04 20:56:32 +02:00
williamandClaude Sonnet 5 00f7191a8b Corrige les animations figées et le reporting SCORM dans le paquet exporté
Build and deploy / test-python (push) Failing after 1m15s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Deux bugs distincts détectés dans le paquet Web/SCORM :
- L'animation de marche du personnage dépend entièrement des touches
  clavier maintenues (heldKeys), qui ne sont alimentées que par des
  écouteurs keydown/keyup sur window. Un LMS comme SCORM Cloud charge
  index.html dans une iframe qui ne reçoit pas le focus clavier par
  défaut : aucune touche n'atteignait jamais window, donc le personnage
  ne bougeait/animait jamais. On force désormais le focus au chargement
  et on le reprend au premier clic dans le jeu.
- scorm-api.js (qui pousse score/statut au LMS) était bien copié dans
  le zip mais jamais référencé par un <script> dans index.html, car
  build_scorm_package.py ne passait pas scorm_api_wrapper_url au
  template — le reporting SCORM (Completion/Success/Score) ne
  fonctionnait donc jamais depuis un export.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 18:23:17 +02:00
williamandClaude Sonnet 5 413685e871 Relie le score/statut des quêtes aux indicateurs SCORM
Build and deploy / test-python (push) Failing after 1m15s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Les quêtes/quiz mettaient à jour l'état local (statut, score) sans
jamais toucher gameData.scoring, que scorm-api.js interroge déjà pour
reporter Completion/Success/Score au LMS. Ajoute forgeSyncQuizScoreToScorm,
forgeSyncQuestStartedToScorm et forgeSyncAllQuestsCompletionToScorm,
appelées lors d'un bon score, de l'acceptation d'une quête et de la
complétion de toutes les quêtes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:56:37 +02:00
williamandClaude Sonnet 5 55c6bb330b Contrôle direct de la taille de la caméra (Largeur × Hauteur, toujours modifiable)
Build and deploy / test-python (push) Failing after 1m13s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Remplace le bouton conditionnel "Agrandir la caméra à la zone visible"
(n'apparaissait que si un fond dépassait déjà la scène) par deux champs
"Caméra (px)" TOUJOURS visibles, pré-remplis avec scene_width/
scene_height actuels — demande explicite : "l'écran de l'éditeur a une
taille fixe, la caméra a la même taille, c'est tout", un réglage direct
plutôt qu'une suggestion automatique liée à la taille d'un fond.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:28:03 +02:00
williamandClaude Sonnet 5 b9d3c46087 Bouton "📐 Agrandir la caméra à la zone visible"
Build and deploy / test-python (push) Failing after 1m23s
Build and deploy / test-js (push) Successful in 54s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Fait de tout le "monde" affiché par l'éditeur (la taille réelle d'un
"fond" plus grand que la scène nominale, voir routes/scenes/
scene_edit_view.py::world_width/world_height) la scène/caméra ELLE-MÊME
— jusqu'ici scene_width/scene_height n'étaient fixées qu'à la création
de l'écran, jamais modifiables ensuite. Le bouton n'apparaît que quand
un fond dépasse encore la scène nominale (même condition que le repère
"🎥 Champ de la caméra"), affiche la taille cible, recharge la page une
fois appliqué. La caméra ne recadre alors plus rien en jeu (monde ==
scène == viewport, voir personnage-controller.js::forgeUpdateSceneCamera).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:07:24 +02:00
williamandClaude Sonnet 5 14ec76dddd Éditeur : le canevas affiche tout le monde (fond agrandi), pas que la scène
Build and deploy / test-python (push) Failing after 1m20s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Suite au fix du glisser figé à (0,0) : le canevas de l'éditeur restait
malgré tout limité à la scène nominale (960×540 par ex.) — la partie
d'un "fond" plus grand qui dépasse restait invisible/injoignable à la
souris. Le canevas affiche désormais tout le "monde" (max de la scène
et de l'étendue de chaque "fond", voir routes/scenes/scene_edit_view.py),
comme la caméra en jeu le fait déjà (personnage-controller.js).

Nouveau repère visuel "🎥 Champ de la caméra" : un cadre en pointillés
marquant où s'arrête la scène nominale à l'intérieur de ce monde agrandi
(le reste est assombri), pour que l'auteur voie clairement ce que le
joueur verra réellement à l'écran une fois le monde plus grand que le
viewport.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:55:09 +02:00
williamandClaude Sonnet 5 6b4846a646 Fix vrai bug : un objet plus grand que la scène (fond/caméra) restait figé à (0,0)
Build and deploy / test-python (push) Failing after 1m22s
Build and deploy / test-js (push) Successful in 56s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Confirmé par l'utilisateur : lié à l'introduction du fond/caméra (voir
add_scene_object.py — un "fond" est posé à sa taille RÉELLE, souvent
bien plus grande que la scène, exprès, pour que la caméra le suive en
défilant sur un monde plus grand que le viewport).

Le glisser en position utilisait Math.max(0, Math.min(SCENE_WIDTH -
width, ...)) — cette formule suppose SCENE_WIDTH - width POSITIF (objet
plus petit que la scène). Pour un objet plus GRAND (ex. un fond de
1920px sur une scène de 960px), cette différence est NÉGATIVE, et
Math.max(0, négatif) ramenait TOUJOURS la position à 0 quel que soit le
glisser — l'objet restait donc figé, impossible à repositionner.

Le redimensionnement plafonnait aussi la largeur/hauteur à "ce qui
reste dans la scène depuis son coin" (SCENE_WIDTH - left) — empêchant
justement de rendre un objet plus grand que la scène par glisser (un
fond ne pouvait être agrandi qu'en le recréant via la galerie).

Les deux bornes sont corrigées pour fonctionner quel que soit lequel
(scène ou objet) est le plus grand.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:43:08 +02:00
williamandClaude Sonnet 5 7c9b2c9b25 Fix "impossible d'aller plus loin vers la droite" — défilement automatique du canevas
Build and deploy / test-python (push) Failing after 1m13s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Cause confirmée (tous les objets bloquaient au même endroit, pas
spécifique à un widget) : une scène à taille fixe (960×540 par ex.)
peut être plus LARGE que la zone visible de .canvasFrame (overflow:auto)
selon la largeur de fenêtre — sa partie droite/basse défile alors HORS
de vue, et le curseur atteignait le bord de cette zone VISIBLE bien
avant celui de la scène elle-même : la souris ne pouvait tout simplement
plus bouger physiquement plus loin, sans aucun rapport avec la limite
réelle (960px) de la scène.

onSceneObjectMouseDown/onSceneObjectResizeMouseDown font maintenant
défiler .canvasFrame automatiquement quand le curseur approche un bord
pendant un glisser/redimensionnement — et continuent de déplacer
l'objet même si la souris reste immobile près du bord (mirror du
comportement standard d'un glisser-déposer dans une zone scrollable),
grâce à une position ACCUMULÉE plutôt que recalculée depuis le point de
départ fixe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:27:51 +02:00
williamandClaude Sonnet 5 046c38b4dc Annule la réservation d'espace des panneaux, corrige le vrai bug (drag invisible sous eux)
Build and deploy / test-python (push) Failing after 1m22s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Retour en arrière sur le commit précédent : les panneaux flottants
doivent RESTER en position:fixed par-dessus le canevas pour maximiser
l'espace utile (remarque explicite) — leur réserver une marge
permanente allait à l'encontre de ce principe et ne réglait rien.

Vrai bug identifié : un objet glissé (déplacé ou redimensionné) sous
l'un de ces panneaux (z-index:60) disparaissait littéralement à
l'écran PENDANT le geste — toujours déplacé/enregistré correctement en
dessous, juste invisible, donnant l'impression qu'on ne pouvait pas y
déposer d'objet. Élevé au-dessus des panneaux (z-index très haut) le
temps du glisser/redimensionnement SEULEMENT, restauré ensuite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:18:42 +02:00
williamandClaude Sonnet 5 d705f58c4a Fix accès au canevas masqué + quiz : header quête, réponse fausse n'avance jamais bloquée, animé
Build and deploy / test-python (push) Failing after 1m36s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
- Bug corrigé : les panneaux flottants "🧩 Objets"/"⚙️ Propriétés" sont en
  position:fixed (hors du flux flex de .builder3) — ils flottaient
  PAR-DESSUS le canevas sans jamais réduire sa largeur, rendant sa
  partie droite/gauche inaccessible au clic/glisser tant qu'un panneau
  restait ouvert. .builderCanvasArea réserve maintenant leur largeur
  (marge) dès qu'un panneau est ouvert (:has()).

- Boîte à quiz : header = "Quête : <titre>" (au lieu du texte de la
  question), corps = la question ET ses choix ensemble.

- Mauvaise réponse : ne bloque plus JAMAIS la progression (bug signalé :
  "je suis obligé de bien répondre sinon j'avance pas") — surligne la
  bonne réponse en vert (le choix cliqué en rouge s'il était faux) puis
  avance automatiquement après un court délai, sans jamais octroyer de
  points. Un second clic pendant la révélation est ignoré.

- Animations : la boîte à quiz rejoue une entrée (pop-in) à CHAQUE
  nouvelle question, la bonne réponse pulse en vert, une mauvaise
  réponse "secoue" en rouge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:08:59 +02:00
williamandClaude Sonnet 5 b5546df044 Quiz jouable : budget de récompense, boîte à quiz, widget score
Build and deploy / test-python (push) Failing after 1m34s
Build and deploy / test-js (push) Successful in 1m1s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
- Récompense d'une quête = budget MAXIMUM pour ses questions : la somme
  des points des "❓ Question" ne peut plus dépasser recompense_score
  (routes/quests/quest_dialogues.py à l'enregistrement des dialogues,
  quest_update.py si on abaisse la récompense sous ce qui est déjà
  réparti) — message d'erreur explicite (400) dans les deux sens,
  affiché via alert() côté éditeur (quest-editor.js), jamais enregistré
  silencieusement dans un état incohérent.
- Nombre de choix par question plafonné à 4 (au lieu de 8).

- Deux nouveaux widgets "🖥️ Interface" (mêmes fondations que "💬 Boîte
  de dialogue" — screens/rendering/dialogue_box_style.py, même panneau
  "🎨 Style") :
  - "❓ Boîte à quiz" : affiche la question et ses choix, sans pied (une
    question se résout au clic sur un choix, pas de "Suivant").
  - "🏆 Score" : affiche en continu les points gagnés, TOUJOURS visible
    une fois posé (contrairement aux boîtes de dialogue/quiz, masquées
    par défaut).

- Moteur d'exécution (static/js/play/dialogue-box-controller.js,
  refonte) : une conversation de quête alterne maintenant répliques
  (boîte de dialogue) et questions (boîte à quiz) selon le type de
  chaque ligne. Bonne réponse -> crédite le score (compteur runtime
  dédié, jamais lié au score du moteur document) et avance ; mauvaise
  réponse -> rien, la question reste affichée pour réessayer ; aucune
  boîte à quiz posée -> la question est ignorée plutôt que de bloquer
  la conversation. Le dialogue "en_cours" épuisé (donc, s'il contenait
  des questions, toutes répondues) fait passer la quête "terminee" (en
  mémoire seulement, comme le reste de ce système — voir accepter/
  refuser une offre de quête) : le joueur peut alors quitter la scène
  normalement, ces widgets n'étant jamais des obstacles.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 15:42:42 +02:00
williamandClaude Sonnet 5 e972acf9e6 Quêtes : bulle "❓ Question" à choix multiples, à côté de "+ Réplique"
Build and deploy / test-python (push) Successful in 6m25s
Build and deploy / test-js (push) Successful in 58s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Nouveau type de ligne dans un dialogue de quête (voir
db/quests/sanitize_quest_dialogues.py) : une bulle JAUNE, dans la même
chaîne verticale que les répliques (même alignement, reliée par un
trait) — header : nombre de choix (2 à 8) + récompense (score, montant
en points) ; body : la question, ses choix et un bouton radio pour
désigner la bonne réponse. Aucune limite au nombre de questions par
colonne, comme pour une réplique.

Les lignes de dialogue portent maintenant explicitement
{"type": "dialogue", ...} (au lieu d'un objet sans type) pour
distinguer les deux formes — migration de forme, tests mis à jour.

Portée de ce commit : l'ÉDITEUR (créer/modifier une question). Le jeu
lui-même (afficher la question au joueur, vérifier la réponse,
attribuer la récompense) reste à construire — le widget "💬 Boîte de
dialogue" ne sait aujourd'hui afficher qu'une réplique.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:48:14 +02:00
williamandClaude Sonnet 5 f2ed602459 Fix boutons Accepter/Refuser absents de l'écran d'offre de quête
Build and deploy / test-python (push) Successful in 6m47s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Le footer de la boîte de dialogue n'avait pas data-dialogue-role="footer"
(seul son bouton interne "Suivant" l'avait, sous "next-btn") — le JS
(forgeShowQuestOffer, dialogue-box-controller.js) le cherche par ce
sélecteur pour y injecter les boutons Accepter/Refuser en fin de
dialogue d'une quête "nouvelle". querySelector ne trouvait donc jamais
le footer, et l'écran d'offre gardait silencieusement le bouton
"Suivant" au lieu des deux nouveaux boutons — exactement le bug signalé.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:04:26 +02:00
williamandClaude Sonnet 5 e90d941527 Quête "nouvelle" : écran Accepter/Refuser à la fin du dialogue
Build and deploy / test-python (push) Successful in 7m13s
Build and deploy / test-js (push) Successful in 1m5s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Une fois le dialogue épuisé, si la quête est encore "nouvelle", la
boîte de dialogue bascule sur un écran d'offre au lieu de se masquer :
header "Quête : <titre>", body l'objectif, pied deux boutons.

- Accepter -> la quête passe "en_cours" (en MÉMOIRE seulement,
  gameData.quests — jamais persisté en base : le statut en base est
  celui de DÉPART pour toute nouvelle partie, pas un état de partie en
  cours, voir PLAYER_SHARED/full_game_payload.py).
- Refuser -> la boîte se referme SANS toucher au statut, qui reste
  "nouvelle" : la prochaine interaction rejoue exactement le même
  dialogue depuis le début — impossible d'avancer la quête sans
  l'accepter un jour.

Pour tout autre statut (déjà "en_cours"/"terminee"), la boîte se
masque normalement à la fin du dialogue, comme avant.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:00:02 +02:00
williamandClaude Sonnet 5 df34106752 Collision : une carte par objet occupe toute la largeur
Build and deploy / test-python (push) Successful in 6m24s
Build and deploy / test-js (push) Successful in 54s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
L'ancienne grille (auto-fill, colonnes de 300px minimum) réservait des
colonnes vides même avec une seule carte, la laissant étroite avec un
grand vide à droite. Empilées en pleine largeur à la place — laisse
aussi la place à une longue chaîne de règles de s'étendre sans se
faire couper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 13:42:36 +02:00
williamandClaude Sonnet 5 aa17614c7b Fix bulle "Appuie sur X" trop haute au-dessus du personnage
Build and deploy / test-python (push) Successful in 6m7s
Build and deploy / test-js (push) Successful in 52s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
La bulle s'ancrait sur le cadre BRUT du sprite (<img> style.left/top/
width/height) — beaucoup de sprites (animaux CraftPix notamment, voir
screens/labels/animal_sprite_library.py) ont un très grand canevas
padé autour d'une silhouette bien plus petite, faisant flotter la
bulle loin au-dessus du personnage visible.

Elle s'ancre maintenant sur la BOÎTE DE COLLISION (objRect, déjà
résolue via forgeElementBoxRect — largeur/hauteur/décalage réglables
dans "🧱 Collision"), que l'auteur ajuste déjà pour épouser la
silhouette réelle : ancre bien plus fidèle, sans configuration
supplémentaire à faire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 13:16:24 +02:00
williamandClaude Sonnet 5 00f9cf33e9 Fix bulle "Appuie sur X" invisible + retire Attaque/Événement du picker
Build and deploy / test-python (push) Successful in 6m12s
Build and deploy / test-js (push) Successful in 52s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Bug corrigé : la bulle "Appuie sur X" était ajoutée comme ENFANT de
l'objet de scène (un <img> pour "personnage"/"decor"/"fond") — un
élément REMPLACÉ dont les enfants DOM ajoutés en JS ne sont JAMAIS
affichés par un navigateur, quel que soit son CSS. La bulle existait
donc bien dans le DOM (aucune erreur) mais restait invisible à l'écran.
Elle est maintenant ajoutée comme SŒUR de l'objet dans son parent
(.sceneWorld/.sceneUI), positionnée en JS aux mêmes coordonnées.

Assistant "+ Action" de l'éditeur de collision : "⚔️ Attaquer la cible"
et "📣 Déclencher un événement" retirés des choix proposés (pas besoin
pour le moment) — le serveur continue d'accepter/d'exécuter une règle
déjà enregistrée avec l'un des deux, rien ne casse pour l'existant.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 12:58:00 +02:00
williamandClaude Sonnet 5 726775fe4e Fix "à la collision" — la marge de 3px restait insuffisante
Build and deploy / test-python (push) Successful in 5m56s
Build and deploy / test-js (push) Successful in 48s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
forgeWouldCollide (personnage-controller.js) bloque le déplacement par
PAS ENTIER (cmd.vitesse px/tick, 4 par défaut mais réglable) : le
joueur peut donc rester bloqué jusqu'à PRESQUE un pas entier de
distance de l'obstacle, pas seulement 1-3px — la marge de tolérance du
correctif précédent (3px) restait insuffisante même pour la vitesse par
défaut dans les cas les moins favorables. Portée à 24px, confortable
pour toute vitesse raisonnablement configurée sans jamais déclencher
"à la collision" alors que les objets sont encore visiblement séparés.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 12:42:17 +02:00
williamandClaude Sonnet 5 3a67ba622b Fix "à la collision" qui ne se déclenchait jamais
Build and deploy / test-python (push) Successful in 6m27s
Build and deploy / test-js (push) Successful in 54s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Cause réelle : un objet portant une règle de collision est presque
toujours AUSSI un obstacle solide (collision activée par défaut, voir
forgeWouldCollide, personnage-controller.js) — le déplacement du joueur
est donc bloqué PILE au contact, sans jamais laisser les deux boîtes se
chevaucher réellement. Le déclencheur "collision" testait un
chevauchement STRICT (forgeShapesOverlap nu), qui n'était donc jamais
atteint : la règle ne se déclenchait jamais en jeu réel, malgré un
contact visible à l'écran.

Le déclencheur teste maintenant la boîte de l'objet élargie d'une
petite marge (3px) — assez pour détecter un simple contact — SANS
toucher au blocage physique du déplacement (resté strict, inchangé).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 12:33:59 +02:00
williamandClaude Sonnet 5 5e17e42dfa Test bout en bout : collision -> quête -> boîte de dialogue
Build and deploy / test-python (push) Successful in 6m11s
Build and deploy / test-js (push) Successful in 58s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Vérifie explicitement le lien moteur demandé : une règle de collision
"quete" retrouve la quête, récupère les répliques de son statut actuel,
les affiche dans l'ordre dans le widget "💬 Boîte de dialogue" posé sur
la scène, avance au clic sur "Suivant", disparaît à la dernière
réplique — déjà implémenté (collision-rules-controller.js +
dialogue-box-controller.js), ce test couvre la CHAÎNE COMPLÈTE plutôt
que chaque brique isolément.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 12:27:50 +02:00