Commit Graph
48 Commits
Author SHA1 Message Date
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 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 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 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
williamandClaude Sonnet 5 077af2234d Fix "+ Réplique" qui remplaçait la bulle au lieu d'en ajouter une
Build and deploy / test-python (push) Successful in 7m9s
Build and deploy / test-js (push) Successful in 1m2s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Bug : questSaveDialogues() adoptait la réponse du serveur comme nouvel
état local aprÚs chaque enregistrement. Une bulle fraßchement ajoutée a
un texte encore vide (rien tapĂ©) — sanitize_quest_dialogues.py la
rejette lĂ©gitimement (jamais de rĂ©plique sans texte persistĂ©e) — donc
la réponse renvoyait un tableau amputé de cette bulle, que le client
adoptait aveuglément, effaçant la bulle en cours d'écriture. Le clic
suivant sur "+ RĂ©plique" repartait donc du mĂȘme Ă©tat qu'avant, semblant
"remplacer" la bulle plutĂŽt que d'en ajouter une seconde. Le client ne
resynchronise plus jamais son Ă©tat depuis la rĂ©ponse d'enregistrement —
il est déjà la seule source de vérité pendant l'édition.

Colonnes de dialogue : occupent maintenant toute la hauteur disponible
de la modale plein écran et défilent chacune indépendamment (titre et
bouton "+ Réplique" restent fixes), au lieu d'une hauteur minimale fixe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 12:08:41 +02:00
williamandClaude Sonnet 5 521792fe00 Dialogues de quĂȘte : bulles illimitĂ©es, n'importe quel objet, timeline reliĂ©e
Build and deploy / test-python (push) Successful in 7m32s
Build and deploy / test-js (push) Successful in 1m2s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
- "qui parle" n'est plus réservé au personnage : N'IMPORTE QUEL objet de
  scĂšne nommĂ© (personnage, dĂ©cor, fond — voir "â„č Informations",
  screens/rendering/scene_object_names.py, ex-personnage_names.py) peut
  parler dans un dialogue.
- Confirmé/documenté : aucune limite au nombre de répliques par colonne
  (bouton "+ Réplique" reste toujours disponible).
- Bulle = header (menu déroulant du "qui parle") + body (le texte),
  centrée dans sa colonne à 70% de largeur, alignée verticalement et
  reliĂ©e Ă  la suivante par un trait — remplace l'ancien alignement
  gauche/droite façon chat.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 11:54:06 +02:00
williamandClaude Sonnet 5 2347a70c45 QuĂȘtes/collision : personnages nommĂ©s, dialogues qui parlent, boĂźte de dialogue en jeu
Build and deploy / test-python (push) Successful in 11m32s
Build and deploy / test-js (push) Successful in 1m12s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
- "â„č Informations" (propriĂ©tĂ©s d'un personnage) : nom Ă©ditable, rĂ©utilisĂ©
  comme "qui parle" dans l'Ă©diteur de dialogue de quĂȘte (menu dĂ©roulant,
  plus "Joueur" toujours disponible) — remplace l'ancien choix binaire
  joueur/pnj. db.sanitize_quest_dialogues accepte maintenant un nom libre.

- Nouveau menu "đŸ–„ïž Interface" (palette d'objets) avec le premier widget :
  "💬 Boüte de dialogue" (kind="dialogue_box", screens/rendering/
  dialogue_box_style.py) — position/taille comme tout objet de scùne,
  panneau "🎹 Style" dĂ©diĂ© (police, taille, Ă©paisseur, couleur du texte,
  couleur header/body/footer). Rendu en <div> Ă  3 zones, jamais soumis Ă 
  la collision ni à l'éditeur de collision (exclu partout : obstacles,
  liste des rÚgles, payload). Rendu cÎté jeu dans .sceneUI, une couche
  FIXE au viewport (jamais .sceneWorld, qui défile avec la caméra).

- static/js/play/dialogue-box-controller.js : fait le lien moteur entre
  l'action "quete" de l'Ă©diteur de collision et ce widget — affiche la
  réplique en cours du dialogue correspondant au STATUT ACTUEL de la
  quĂȘte (gameData.quests, dĂ©sormais exposĂ© game-wide par
  full_game_payload.py), avance au clic sur "Suivant" (header = qui
  parle, body = texte, footer = bouton), se masque à la derniÚre réplique.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 11:20:58 +02:00
williamandClaude Sonnet 5 390ee831ca QuĂȘtes : dialogue dans une modale Ă  part, en plein Ă©cran
Build and deploy / test-python (push) Successful in 6m17s
Build and deploy / test-js (push) Successful in 1m14s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Sépare la modale unique en deux étapes distinctes demandées par
l'utilisateur : #questFieldsModal ("je crĂ©e" — titre/objectif/
récompense/statut/résultat, taille normale) puis, sur un bouton dédié
"💬 Ajouter les dialogues", #questDialogueModal ("j'ajoute les
dialogues" — les 3 colonnes de rĂ©pliques SEULES, occupant tout
l'Ă©cran). Un bouton "←" ramĂšne Ă  la fiche ; "✓ Utiliser cette quĂȘte"
reste disponible dans les deux, pour l'intégration avec l'éditeur de
collision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 20:54:00 +02:00
williamandClaude Sonnet 5 78c1aba8a0 Éditeur de quĂȘtes : onglet Ă  cĂŽtĂ© de Collision, pas une page Ă  part
Build and deploy / test-python (push) Successful in 6m7s
Build and deploy / test-js (push) Successful in 59s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Corrige la mauvaise interprétation de la demande initiale : la page
dĂ©diĂ©e "đŸ—ș QuĂȘtes" (nav du jeu) est retirĂ©e au profit d'un nouvel
onglet "đŸ—ș QuĂȘtes" DANS l'Ă©diteur de scĂšne 2D, juste Ă  cĂŽtĂ© de
"đŸ§© Collision" — mĂȘme liste tabulaire que l'onglet "📣 ÉvĂ©nements"
(titre/objectif/récompense/statut/résultat par ligne, pas des cartes),
un clic sur une ligne ouvrant la modale d'arbre de dialogue déjà
construite. L'assistant "+ Action" de l'éditeur de collision ouvre
toujours cette mĂȘme modale pour "DĂ©clencher une quĂȘte".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 20:46:27 +02:00
williamandClaude Sonnet 5 51427b5297 Éditeur de quĂȘtes : titre/objectif/rĂ©compense/statut/rĂ©sultat + dialogue Ă  3 colonnes
Build and deploy / test-python (push) Successful in 6m14s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Nouvel espace "đŸ—ș QuĂȘtes" (game-wide, comme les Ă©vĂ©nements
personnalisĂ©s) : liste de cartes + "+ Nouvelle quĂȘte", chaque carte
ouvrant une modale d'arbre de dialogue en bulles de théùtre
("speaker: texte"), une colonne par statut de quĂȘte (nouvelle/en
cours/terminée) puisque le dialogue joué dépend de l'état de la
quĂȘte au moment oĂč le joueur parle au PNJ. Bulles alternĂ©es
joueur/PNJ, couleur différente selon qui parle.

L'action "DĂ©clencher une quĂȘte" de l'Ă©diteur de collision n'est plus
un champ texte libre : elle ouvre directement CETTE MÊME modale
(picker de quĂȘtes existantes + crĂ©ation Ă  la volĂ©e), et finalise la
rĂšgle avec l'id numĂ©rique de la quĂȘte choisie — collision_rules.py
migré en conséquence (quete_id devient un entier, comme evenement_id).

Corrige aussi la miniature d'objet de la carte "đŸ§© Collision", 3× plus
grande (120px) comme demandé.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 20:24:57 +02:00
williamandClaude Sonnet 5 b64541856f Éditeur de collision : cartes "piĂšces qui s'emboĂźtent", animĂ©es
Build and deploy / test-python (push) Successful in 6m27s
Build and deploy / test-js (push) Successful in 56s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Refonte visuelle de l'onglet "đŸ§© Collision" — l'ancien rendu (pastilles
plates + flÚches) ne rendait pas la métaphore "piÚces de puzzle qui
s'emboßtent" demandée. Chaque maillon (déclencheur/action/sous-action)
est maintenant une piÚce chevronnée colorée par type, imbriquée dans
la suivante via clip-path + marge négative, avec une entrée animée en
cascade. Cartes objet et choix de l'assistant modal redessinés en
grille de cartes avec icÎne + libellé, hover/entrée animés.

Purement visuel — aucun changement de logique/donnĂ©es (dĂ©jĂ  couvert
par tests/test_collision_rules.py et
static/js/play/__tests__/collision-rules-controller.test.js).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 17:23:42 +02:00
williamandClaude Sonnet 5 ee270d0286 Éditeur de collision (jeu 2D) : rùgles trigger -> action par objet
Build and deploy / test-python (push) Successful in 6m31s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Nouvel onglet "đŸ§© Collision" dans l'Ă©diteur de scĂšne 2D : chaque objet
(hors joueur et fond) peut porter des rĂšgles "Ă  la collision" ou "dans
un pĂ©rimĂštre (px)" dĂ©clenchant une action (quĂȘte, attaque, Ă©vĂ©nement,
ou interagir - qui affiche "Appuie sur [touche]" puis exécute une
sous-action Ă  l'appui, un seul niveau d'imbrication). Backend
(sanitisation, route de persistance, exposition dans
full_game_payload) + assistant modal en cartes empilées cÎté client.

Ajoute le moteur d'exécution runtime (collision-rules-controller.js) :
détecte l'entrée en collision/périmÚtre avec le joueur à chaque tick,
déclenche l'action une seule fois par entrée, pur JS client sans appel
serveur (fonctionne Ă  l'identique en ligne et en export SCORM).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 17:11:42 +02:00
williamandClaude Sonnet 5 dfaf85fac1 Retire la collision sur les images de fond + les blocs de logique/timeline de l'éditeur 2D
Build and deploy / test-python (push) Successful in 6m55s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Deux retours utilisateur sur l'éditeur de scÚne 2D :

- Le panneau "đŸ§± Collision" (rĂ©glages + aperçu visuel sur le canevas)
  n'apparaĂźt plus pour un objet kind="fond" : jamais un obstacle ni une
  cible de collision possible (déjà exclu de forgeSolidObstacles cÎté
  jeu), ce panneau n'aurait donc aucun effet.

- Retire les onglets "đŸ§© Blocs de logique" et "🎬 Timeline d'animation"
  de CET éditeur (l'éditeur document, templates/screen_edit.html, les
  garde tel quel) — en prĂ©vision d'un Ă©diteur dĂ©diĂ© aux scĂšnes 2D
  (collision/périmÚtre -> action), pas encore construit. switchBuilderTab()/
  toggleDashCreate() (gĂ©nĂ©riques, nĂ©cessaires Ă  l'onglet "ÉvĂ©nements"
  restant) extraites dans un nouveau builder-tabs.js partagé par les deux
  éditeurs, pour ne plus avoir à charger flow-editor.js/tabs-and-blocks.js/
  animation-timeline.js (propres aux blocs/timeline) dans l'éditeur 2D.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 16:41:56 +02:00
williamandClaude Sonnet 5 a231bcf2fa Aperçu de la boßte de collision dans l'éditeur + blocage physique au jeu
Build and deploy / test-python (push) Successful in 6m15s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Deux retours utilisateur sur la boßte de collision (ajoutée précédemment) :

- Visible et réglable directement dans l'éditeur de scÚne : pour l'objet
  sélectionné, un contour pointillé (rectangle/cercle) se superpose sur
  le canevas — glisser son corps ajuste le dĂ©calage, glisser sa poignĂ©e
  (coin bas-droit) ajuste la taille, les deux se répercutent dans le
  panneau "đŸ§± Collision" et inversement (Ă©dition des champs -> aperçu Ă 
  jour). static/js/scenes/scene-editor.js::onCollisionBoxMouseDown/
  onCollisionBoxResizeMouseDown, mirror des poignées de position/taille
  dĂ©jĂ  existantes pour l'objet lui-mĂȘme.

- Bloque dĂ©sormais RÉELLEMENT le dĂ©placement au clavier : avant chaque
  pas, personnage-controller.js teste si la position candidate
  chevaucherait la boĂźte de collision d'un autre objet solide (jamais un
  "fond", jamais un objet Ă  collision dĂ©sactivĂ©e) et annule ce pas — par
  AXE séparément, pour permettre de glisser le long d'un mur en
  diagonale plutÎt qu'un blocage total au moindre contact. Réutilise
  forgeShapesOverlap (conditions.js, factorisé depuis elementsOverlap
  pour ne jamais dupliquer la rĂšgle "qu'est-ce qui se touche").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 15:48:08 +02:00
williamandClaude Sonnet 5 bc3feb5080 Panneau scÚne simplifié + boßte de collision + rÎle joueur/ennemi/pnj
Build and deploy / test-python (push) Successful in 11m52s
Build and deploy / test-js (push) Successful in 56s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Trois retours utilisateur sur l'éditeur de scÚne 2D :

- Retire l'arborescence "Objets de cette scĂšne" (un objet reste
  sélectionnable en cliquant dessus sur le canevas) et le bouton
  "+ Ajouter un décor" (le type "decor" reste supporté cÎté serveur,
  juste plus accessible depuis ce menu).

- Chaque objet de scÚne a désormais une boßte de collision AUTOMATIQUE
  (= sa propre boßte, comportement inchangé pour une condition de
  collision dĂ©jĂ  posĂ©e) rĂ©glable dans un nouveau panneau "đŸ§± Collision" :
  activĂ©e/dĂ©sactivĂ©e, forme (rectangle/cercle), taille et dĂ©calage —
  utile pour un sprite trÚs paddé (CraftPix) dont la silhouette réelle
  est bien plus petite que son canevas. elementsOverlap() (conditions.js)
  applique ces réglages en restant identique par défaut.

- Nouveau champ "đŸ·ïž RĂŽle" (Joueur/Ennemi/PNJ) sur un personnage : SEUL un
  personnage "Joueur" est désormais déplacé/animé au clavier et suivi
  par la camĂ©ra (personnage-controller.js) — "Ennemi"/"PNJ" restent
  immobiles tant qu'aucune logique de flow ne les pilote (déplacement
  ennemi automatique/apparition, dialogue de PNJ... hors scope de ce
  réglage, qui ne fait qu'identifier "quel objet est le joueur").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 15:30:34 +02:00
williamandClaude Sonnet 5 9199897784 Image de fond de scÚne + caméra qui suit le personnage, fps d'animation cohérent
Build and deploy / test-python (push) Successful in 6m26s
Build and deploy / test-js (push) Successful in 57s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Trois retours utilisateur :
- fps d'idle/interagir/touches supplémentaires alignés sur celui de la
  marche (8 i/s partout, Ă©tait 4 pour idle — perçu comme "les autres
  animations sont lentes à cÎté de la marche").
- Nouveau menu "đŸžïž Images de fond" dans "Objets de cette scĂšne" : pose
  un objet kind="fond" à la taille RÉELLE de l'image choisie (pack
  CraftPix intĂ©grĂ© en galerie, admin seulement — licence, voir
  core/sprite_gate.py), derriĂšre tout le reste, insensible au clic.
- Si ce fond dépasse la scÚne, elle devient le "monde" : .playScreen.
  playScene est désormais le viewport (overflow:hidden, taille fixe),
  .sceneWorld le monde Ă  l'intĂ©rieur — la camĂ©ra centre le premier
  personnage trouvé, bornée pour ne jamais montrer au-delà des bords
  (voir personnage-controller.js::forgeUpdateSceneCamera). Le personnage
  peut désormais se déplacer sur tout le monde, pas seulement le petit
  cadre visible (clampSceneObjectPosition, actions.js). Un jeu sans fond
  XXL garde un comportement strictement identique Ă  avant (monde ==
  scĂšne, transform vide).

BibliothÚque générée une fois par scripts/generate_background_manifest.py
(assets/background/, non versionné, licence CraftPix) vers
static/backgrounds/ (committĂ©), mĂȘme patron que
generate_animal_sprite_manifest.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 12:24:10 +02:00
williamandClaude Sonnet 5 5891c82c62 Commandes personnage : détection de touche au clavier + touches d'animation supplémentaires
Build and deploy / test-python (push) Successful in 6m17s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Deux retours utilisateur sur le panneau "Commandes" (déplacement/
animation automatiques, voir précédent commit) :
- Les champs de touche (haut/bas/gauche/droite/interagir) capturent
  maintenant la touche au clavier (clic puis appui — event.key, mĂȘme
  valeur que heldKeys/triggers.js) au lieu d'ĂȘtre tapĂ©s Ă  la main,
  source d'erreurs ("Espace" vs " ", "flĂšche haut" vs "ArrowUp"...).
- Nouvelle section "Animations supplémentaires" : un nombre illimité de
  touches, chacune liée à UNE pose au choix parmi celles réellement
  disponibles pour ce personnage (sauter, attaquer, courir...) — pas
  seulement les 4 touches de déplacement + interagir.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 11:50:29 +02:00
williamandClaude Sonnet 5 74f7ca396f Déplacement/animation automatiques d'un personnage (scÚne 2D)
Build and deploy / test-python (push) Successful in 6m25s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Simplification demandée pour l'éditeur de jeu RPG : un personnage posé
sur une scÚne 2D se déplace et s'anime TOUT SEUL avec ZQSD (+ E pour
interagir) dĂšs qu'il est posĂ© — plus besoin de poser le moindre nƓud de
flow pour un mouvement de base. Le panneau "đŸ•č Commandes" du personnage
permet de remapper les 4 touches de déplacement et la touche
d'interaction, et de bloquer un axe (horizontal/vertical seulement).

EntiÚrement client (static/js/play/personnage-controller.js), réutilise
heldKeys (triggers.js) et applyObjectProperty/clampSceneObjectPosition/
runSpriteAnimation (actions.js) — aucune logique dupliquĂ©e, et ça
fonctionne aussi bien en ligne que dans l'export Web/SCORM (aucune
requĂȘte serveur). "walk"/"idle"/"interact" (poses dĂ©jĂ  prĂ©sentes pour
tout personnage Forge/CraftPix) sont utilisées telles quelles ; une pose
absente dégrade silencieusement (déplacement sans animation) plutÎt que
de planter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 11:26:37 +02:00
williamandClaude Sonnet 5 82d00b3800 Export Web/SCORM : inline les icĂŽnes en data: URI (CORS Firefox/file://)
Build and deploy / test-python (push) Successful in 6m16s
Build and deploy / test-js (push) Successful in 54s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Le chemin relatif ("icons/x.svg") était enfin correct, mais Firefox
bloque encore par CORS TOUT chemin de fichier pour mask-image sous
file:// — chaque ressource file:// y est une origine opaque distincte,
indĂ©pendamment du chemin (troisiĂšme rapport du mĂȘme utilisateur). Seule
une data: URI (aucune requĂȘte rĂ©seau sĂ©parĂ©e) contourne le problĂšme.
_build_icon_data_uris (build_scorm_package.py) encode chaque icĂŽne
RÉELLEMENT posĂ©e dans le jeu (Ă©lĂ©ment direct ou nichĂ© dans un Ă©lĂ©ment de
jeu réutilisable) en base64, ajouté au payload sous
gameData.icon_data_uris ; forgeRenderIcone (JS) le consulte pour toute
régénération aprÚs une action, avec repli sur l'ancien chemin relatif
si jamais une icÎne n'a pas été pré-encodée.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 10:27:04 +02:00
williamandClaude Sonnet 5 b60fe63a54 Export Web/SCORM : corrige le chemin de l'icÎne doublé "static/static/"
Build and deploy / test-python (push) Successful in 6m44s
Build and deploy / test-js (push) Successful in 56s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Le premier correctif ("static/icons/x.svg") ne suffisait pas : cette URL
est posĂ©e en style inline (--icon-url) mais CONSOMMÉE par `mask-image:
var(--icon-url)` dans static/style.css (.icon-svg) — une URL relative
dans une propriété personnalisée CSS se résout par rapport à la feuille
de style oĂč le var() est UTILISÉ, pas oĂč elle est dĂ©finie (piĂšge CSS
connu). Résultat : "static/icons/x.svg" redevenait
"static/static/icons/x.svg" une fois résolu depuis static/style.css
(CORS bloquĂ© — deuxiĂšme rapport du mĂȘme utilisateur). Seul "icons/x.svg"
(sans le préfixe "static/") est correct une fois résolu depuis
static/style.css. Corrigé cÎté rendu initial
(_relativize_absolute_urls) et cÎté port JS (forgeRenderIcone, qui
régénÚre ce widget aprÚs une action).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 10:12:44 +02:00
williamandClaude Sonnet 5 e08c53e042 Export Web/SCORM : corrige la résolution des champs "relation" hors ligne
Build and deploy / test-python (push) Successful in 6m55s
Build and deploy / test-js (push) Successful in 1m4s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Bug signalé : une Donnée liée/un filtre référençant un champ "relation"
affichait "{{champ}}" tel quel une fois exporté, alors qu'il fonctionnait
en ligne. Deux causes cumulées :

- full_game_payload.py lisait la colonne SQL "<champ>" au lieu de
  "<champ>_id" (seule vraie colonne d'un champ relation, voir
  _field_column dans filter_repeater_rows.py) en construisant
  gameData.data — une valeur toujours None. Invisible en ligne (les
  filtres y requĂȘtent la base fraĂźche, jamais cette snapshot), mais
  fatal hors ligne (aucune base Ă  requĂȘter).
- Une fois ce None corrigé, le port JS (forgeFieldColumn) relisait
  cette mĂȘme valeur sous une clĂ© slugifiĂ©e+suffixĂ©e ("boss_id") alors
  que gameData.data est déjà indexé par le nom D'AFFICHAGE du champ
  ("boss") — mismatch qui ne se voyait que sur un champ relation (le
  seul cas oĂč slugify(nom)+suffixe diverge du nom original). Supprime
  ce port erroné, lit directement row[fieldName] partout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 10:02:26 +02:00
williamandClaude Sonnet 5 7bc4c89eaa Export Web/SCORM : corrige les URLs absolues cassées en local (file://)
Build and deploy / test-python (push) Successful in 13m15s
Build and deploy / test-js (push) Successful in 1m4s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Bug signalé : icÎne disparue et texte suivant du dialogue absent aprÚs
export. render_icone.py (et son miroir JS, forgeRenderIcone) bakaient
une URL "/static/icons/..." en dur, correcte en ligne (racine du
serveur Flask) mais bloquée par CORS une fois ouverte en local
(file:///static/...). MĂȘme souci pour les fichiers envoyĂ©s par le
créateur (/game/<slug>/uploads/..., jamais dans static/, jamais copiés
par le paquet). Ajoute _relativize_absolute_urls (réécriture globale
post-rendu du HTML) + copie du dossier uploads/, et relativise le port
JS de l'icÎne pour toute régénération aprÚs action en mémoire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 09:31:44 +02:00
williamandClaude Sonnet 5 f3b72d73c9 Export Web/SCORM : port complet du runtime jouable cÎté navigateur
Build and deploy / test-python (push) Successful in 9m20s
Build and deploy / test-js (push) Successful in 1m6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Phase 1.2 de la feuille de route produit — un paquet SCORM tourne seul
dans le LMS du client, sans serveur Forge Engine disponible. Ajoute
static/js/play/offline/ (miroir JS de screens/rendering/ et
data_actions/, sous window.FORGE_OFFLINE) pour que variables, score,
données d'objet, répéteurs et conditions de visibilité fonctionnent
entiÚrement en mémoire cÎté navigateur ; branche actions.js et
bindings.js dessus au lieu d'un fetch() serveur. Ajoute
publish/build_scorm_package.py (paquet statique + imsmanifest.xml
SCORM 1.2 + wrapper API SCORM) et le bouton "Exporter (Web/SCORM)".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 07:43:02 +02:00
williamandClaude Sonnet 5 9776fbc057 Score/Progression (Phase 1.1 de la feuille de route produit)
Build and deploy / test-python (push) Successful in 7m27s
Build and deploy / test-js (push) Successful in 1m16s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Concept de premier ordre, distinct du systùme de variables globales —
préalable identifié aux futurs exports SCORM/xAPI (note de cadrage
.claude/Forge_Engine_Cadrage.pdf) : ces standards ont besoin d'un signal
"score"/"terminé" propre, pas d'une convention sur une variable choisie
par le créateur.

- db/scoring/ (mĂȘme patron que db/global_vars/) : table _scoring, un
  score numérique + un statut (non_commence/en_cours/termine/reussi/
  echoue) par joueur (voir db.PLAYER_SHARED pour l'aperçu créateur).
- Deux nouvelles actions de flux, dans les deux éditeurs (document et
  scÚne) : "Modifier le score" (réutilise le vocabulaire d'opérations
  dĂ©jĂ  lĂ  pour "Modifier une variable" — incrĂ©menter/dĂ©finir/etc., aucune
  nouvelle colonne de nƓud) et "DĂ©finir le statut de la partie".
- Routes créateur (routes/flow/flow_node_run_score.py,
  flow_node_run_status.py) + miroirs publics par joueur
  (routes/public_play/) — mĂȘme principe que flow_node_run_variable.py.
- GET /game/<slug>/scoring/<player_id> : lecture interne, pas exposée au
  joueur, prĂ©parĂ©e pour ĂȘtre consommĂ©e par le futur export Web/SCORM.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 06:23:37 +02:00
williamandClaude Sonnet 5 76a50fa87a Onboarding guidé systématique + tableau de bord simplifié
Build and deploy / test-python (push) Failing after 1m48s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Comportement SYSTÉMATIQUE Ă  chaque crĂ©ation de jeu (admin compris), pas
une formalité réservée à l'inscription : /onboarding (routes/onboarding/)
devient le point d'entrĂ©e unique — 4 cartes retournables (survol =
explication au dos), défilement horizontal animé vers le nom du jeu. Un
admin y repasse à volonté (pas de project_slug dédié, jamais bloqué/
redirigé vers un projet précédent) ; un compte "user" n'en a plus qu'un
créé d'office (routes/auth/register_2fa.py), guidé ici à la place.

- db/games/game_type_catalog.py : catalogue des 4 types (Quiz/
  Embranchement-escape game/RPG/Créer mon jeu de A à Z), _meta['onboarding_type']
  décide du "kind" du premier écran créé et si le tableau de bord complet
  reste accessible.
- routes/games/game_dashboard.py, templates/game_dashboard_simple.html :
  un type restreint (quiz/embranchement/rpg) voit désormais SON tableau
  de bord (mĂȘme route que "custom"), rendu en version simplifiĂ©e — juste
  ses Ă©crans en cartes avec un aperçu RÉEL du contenu (scĂšne mise Ă 
  l'échelle par container query CSS, adaptée à la largeur réelle de la
  carte). "+ Ajouter un écran" n'y propose pas de choix de type : imposé
  par le projet (routes/screens/screens_new.py), verrouillé aussi cÎté
  serveur.
- core/auth_guard.py : plus de blocage de game_dashboard par type — la
  restriction se fait au rendu, pas Ă  l'accĂšs Ă  la route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 06:22:45 +02:00
williamandClaude Sonnet 5 d5a59bdbd8 Fusion des deux moteurs : type d'écran par écran, plus par projet
Build and deploy / test-python (push) Failing after 6m10s
Build and deploy / test-js (push) Successful in 58s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
"document" (écrans %) et "jeu_2d" (scÚne pixels) devient une propriété
PAR ÉCRAN (_screens.kind, migration automatique idempotente dans
ensure_schema.py, source = l'ancien game_type au niveau projet) plutĂŽt
qu'un choix figĂ© pour tout le jeu — un mĂȘme projet peut dĂ©sormais
mélanger écrans classiques et scÚnes 2D librement.

- routes/screens/screen_edit.py : dispatch vers l'éditeur de scÚne selon
  screen["kind"] (l'écran demandé), plus game["game_type"].
- screens/payload/full_game_payload.py, templates/play.html,
  static/js/play/screens.js : le rendu jouable (payload, markup, bascule
  du mode plein-écran #playFrame) décide écran par écran, y compris en
  cours de partie (changer d'écran ne recharge pas la page).
- screens/screens_repo/create_screen.py : nouveau paramĂštre kind.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 06:21:13 +02:00
williamandClaude Sonnet 5 727af55a5e Mouvement continu, limites de scÚne et priorité d'animation (jeu 2D)
Build and deploy / test-python (push) Failing after 1m46s
Build and deploy / test-js (push) Successful in 6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Répond au manque signalé par l'utilisateur : le déclencheur "clavier"
existant (keydown) ne se déclenche qu'UNE FOIS par appui, insuffisant
pour "maintenir une touche fait avancer/sauter le personnage en continu".

- Deux nouveaux déclencheurs (screens/flow/constants.py,
  screens/scenes/flow_palette.py) : "Tant qu'une touche est maintenue"
  (se répÚte ~20 fois/seconde tant que la touche reste enfoncée,
  runScreenHeldKeyTriggers() dans triggers.js — mĂȘme patron PAR ÉCRAN que
  "minuteur", arrĂȘtĂ© au changement d'Ă©cran) et "Au relĂąchement d'une
  touche" (un seul déclenchement, scan global comme "clavier"). Combinés
  Ă  l'action existante "Modifier un objet de scĂšne → DĂ©placer de... px
  (relatif)", ça permet un vrai déplacement continu.
- preventDefault() sur toute touche que le jeu écoute réellement
  (isGameKey(), triggers.js) : Espace/FlÚches font défiler la page par
  défaut, et Espace réactive en plus le bouton actuellement focus (souvent
  le bouton "Jouer" qui garde le focus aprùs l'ouverture de l'aperçu) —
  ça pouvait donner l'impression qu'une touche du jeu ne faisait rien.
- Le personnage pouvait sortir du cadre de la scÚne en se déplaçant :
  applyObjectProperty()/clampSceneObjectPosition() (static/js/play/actions.js)
  bornent maintenant toute position (absolue ou relative) Ă 
  [0, scene_width/height − la taille de l'objet].
- Vitesse d'animation par défaut adaptée au nombre d'images : la valeur
  fixe (8 i/s) venait d'un formulaire pensé pour les cycles Kenney (8
  images) — un cycle CraftPix (walk=30 images) au mĂȘme 8 i/s prenait
  ~4 secondes, "trĂšs lent". Le choix d'une animation dans la galerie
  calcule maintenant une vitesse par défaut proportionnelle à son nombre
  d'images (flow-editor.js, animation-timeline.js).
- Priorité d'animation (bug : "je ne peux pas me déplacer et sauter") :
  un déclencheur de déplacement (touche maintenue) redemande "marche" à
  chaque tick, écrasant aussitÎt une animation ponctuelle ("sauter")
  démarrée entre-temps avant qu'elle ait pu s'afficher. runSpriteAnimation()
  (actions.js) laisse maintenant une animation NON BOUCLÉE en cours
  (mĂȘme Ă  une seule frame, ex. une pose Kenney figĂ©e) aller jusqu'au bout
  avant qu'une autre demande puisse l'interrompre.

Nouveaux tests : static/js/play/__tests__/{actions,triggers}.test.js
(idempotence + priorité d'animation, bornage aux limites de la scÚne,
isGameKey) ; tests/test_scene_edit_view.py (persistance d'un nƓud
"touche_maintenue").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 21:53:49 +02:00
williamandClaude Sonnet 5 f0070faced Phase A (2/2) : Ă©diteur de scĂšne 2D — route, template, runtime jouable
Build and deploy / test-python (push) Successful in 1m56s
Build and deploy / test-js (push) Successful in 8s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
DeuxiĂšme et dernier commit de la Phase A du plan "Fondations d'une
plateforme multi-éditeurs" (le premier, efa7d1d, posait le schéma et le
CRUD des objets de scÚne). Ce commit branche l'éditeur et le mode
jouable sur ces fondations, en réutilisant TEL QUEL tout ce qui est déjà
générique cÎté moteur de logique.

Éditeur (routes/scenes/, templates/scene_edit.html,
static/js/scenes/scene-editor.js) :
- routes/screens/screen_edit.py délÚgue à render_scene_edit() dÚs que
  game_type == "jeu_2d" — mĂȘme endpoint Flask "screen_edit" pour les
  deux éditeurs, aucune route dupliquée.
- Nouveau template scene_edit.html : canevas Ă  taille FIXE en pixels
  (scene_width/scene_height) avec glisser-déposer/redimensionnement en
  pixels (scene-editor.js, mirror pixel de tree-panels.js), galerie
  personnages/décors. Les onglets "Blocs de logique"/"Timeline
  d'animation"/"ÉvĂ©nements" rĂ©utilisent tels quels flow-editor.js,
  tabs-and-blocks.js et animation-timeline.js.
- flow-editor.js : nouveau flag FLOW_TARGETS_OBJECTS (false par défaut,
  true dans scene_edit.html) qui fait écrire submitNodeForm() vers
  trigger_object_id/target_object_id au lieu de trigger_element_id/
  target_element_id, plus une branche "Modifier un objet de scĂšne"
  (position px, visibilité, orientation) et des gardes null partout
  (le DOM de scene_edit.html n'a pas tous les champs de l'éditeur
  document).
- blocks_view.py étend l'ensemble d'ids d'éléments concernés par un
  bloc pour inclure aussi trigger_object_id/target_object_id.

Runtime jouable (templates/play.html, static/js/play/actions.js,
screens/payload/full_game_payload.py) :
- full_game_payload() construit les écrans d'un jeu jeu_2d à partir de
  _scene_objects (render_scene_object) au lieu de _screen_elements, et
  récupÚre les animations de personnage sur les objets de scÚne.
- play.html : #playFrame occupe tout le viewport pour une scĂšne jeu_2d
  (pas de ratio fluide) ; la scĂšne se centre via .playScreen.playScene,
  qui réutilise tel quel showScreen() (déjà indexé par id d'écran, pas
  par forme DOM) — aucun fichier "scenes.js" sĂ©parĂ© n'a Ă©tĂ© nĂ©cessaire.
- actions.js : jouer_animation_sprite retombe sur target_object_id si
  target_element_id est absent ; nouvelle action modifier_objet_scene
  avec applyObjectProperty() (positions en px, contrairement aux % de
  applyElementProperty()).

Un bug de gabarit a été découvert et corrigé pendant la vérification :
le commentaire CSS de play.html contenait littéralement "{% for %}"
comme texte français, que Jinja interprétait comme une vraie balise et
faisait planter le rendu — reformulĂ©. render_scene_object() n'Ă©tait en
outre jamais appelé par la vue de l'éditeur (les objets de scÚne
s'affichaient sans image) — scene_edit_view.py attache maintenant
rendered_html Ă  chaque objet avant de les passer au template.

Nouveaux tests (tests/test_scene_edit_view.py, 10 cas) : dispatch
document vs jeu_2d, rendu d'un personnage sélectionné, CRUD géométrie/
suppression d'objet, nƓuds de flow ciblant un objet de scùne (action et
condition de collision), payload et page /play pour un jeu jeu_2d,
présence des nouveaux helpers JS. Suite complÚte : 309 tests passent
(299 existants + 10 nouveaux, aucune régression). node --check et
node --test (13 tests JS) passent également.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 10:05:40 +02:00
williamandClaude Sonnet 5 70c2b8df05 Corrige : une animation déjà posée dans la logique ignorait le changement de personnage
Build and deploy / test-python (push) Successful in 1m39s
Build and deploy / test-js (push) Successful in 7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Bug réel identifié grùce à la vidéo fournie + inspection directe de la
base de jeu de test : quand un nƓud de flow "Jouer une animation"
(ou un clip de Timeline "sprite") était configuré pour un personnage,
ses frames Ă©taient rĂ©solues et FIGÉES dans data_value/custom_keyframes
au moment de la configuration — changer ensuite le personnage Forge de
l'élément (galerie des propriétés) n'avait donc aucun effet sur les
animations déjà posées, qui continuaient à jouer indéfiniment les
frames de l'ANCIEN personnage.

Le nƓud/clip ne stocke dĂ©sormais que le NOM de l'animation
({"animation": "walk", "fps": 8, "loop": true}) — ses frames sont
rĂ©solues Ă  l'EXÉCUTION, Ă  partir du personnage ACTUELLEMENT assignĂ© Ă 
l'élément cible :
- screens/payload/full_game_payload.py expose un nouveau
  gameData.personnage_animations (Ă©lĂ©ment → animations), reconstruit Ă 
  chaque chargement de la page de jeu depuis _personnage_data — donc
  toujours Ă  jour, y compris aprĂšs un changement de personnage.
- static/js/play/actions.js (resolveSpriteFrames) et
  static/js/play/screens.js (applyAnimationClip) résolvent le nom
  d'animation en frames à ce moment précis, plutÎt que d'utiliser des
  frames figĂ©es — repli sur l'ancien format {frames,...} pour les
  nƓuds/clips dĂ©jĂ  créés avant ce correctif.
- Éditeur (flow-editor.js/animation-timeline.js) : simplifiĂ© en
  consĂ©quence — plus besoin de deviner rĂ©troactivement quelle animation
  correspond à une liste de frames stockées (l'ancien hack de
  comparaison), le nom est maintenant stocké directement.

Nouveau test de régression (test_swapping_forge_character_updates_
already_configured_flow_action) qui reproduit exactement le scénario
filmé : configure l'action pour "male-adventurer", change le personnage
en "zombie", vérifie que gameData.personnage_animations reflÚte bien
zombie sans avoir à retoucher le nƓud de flow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 08:56:20 +02:00
williamandClaude Sonnet 5 5ead091c75 Personnages : toutes les animations, tous les personnages ; retrait de l'import de sprites personnalisés
Build and deploy / test-python (push) Successful in 1m50s
Build and deploy / test-js (push) Successful in 6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
À la demande de l'utilisateur (validation de la refonte visuelle du
widget Personnage), deux ajustements :

- BibliothÚque de sprites Forge étendue aux 6 personnages du pack Kenney
  "Toon Characters" (aventurier/aventuriĂšre, personnage homme/femme,
  robot, zombie), avec TOUTES leurs poses (45 par personnage) plutĂŽt que
  3 — regroupĂ©es en 31 animations nommĂ©es par personnage (poses
  numérotées type walk0..walk7 fusionnées en un seul cycle "walk", les
  autres restant des poses figées à une image). ~1,2 Mo au total,
  toujours un sous-ensemble curé du pack source (assets/characters/, non
  versionnĂ©) — HD/Parts/Tilesheet/Vector toujours exclus.
- Import de sprites personnalisés retiré pour le moment : plus de tuile
  "➕ Sprite personnalisĂ©" dans la galerie d'ajout, plus de section
  d'import dans les propriĂ©tĂ©s d'un personnage — seuls les personnages
  Forge restent proposés. La résolution serveur de _personnage_data
  garde son support générique de la source "custom" (aucune migration
  requise si cette possibilité revient plus tard), mais plus aucune UI
  ne permet de la créer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 07:38:22 +02:00
williamandClaude Sonnet 5 9e076f8cf9 Refonte : vrai widget "Personnage" visuel (remplace l'UI de la Phase 7)
Build and deploy / test-python (push) Successful in 1m26s
Build and deploy / test-js (push) Successful in 6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
L'utilisateur a testé la Phase 7 (sprites pilotés via un menu déroulant
+ zone de texte dans la logique de flow) et l'a rejetée à raison : ce
n'est pas comme ça qu'un moteur de jeu (Phaser, Unity) gÚre un
personnage. Cette refonte remplace tout le flux d'AUTEURING par un vrai
widget visuel — le moteur d'exĂ©cution de la Phase 7 (runSpriteAnimation,
activeSpriteAnimations, orientation, kind="sprite" de la Timeline) reste
inchangé.

Nouveau widget "personnage" (screens/widgets/registry.py) :
- Rendu serveur dédié (special_render, screens/rendering/render_personnage.py)
  qui affiche la pose "idle" dĂšs le HTML gĂ©nĂ©rĂ© — jamais une image
  cassée à configurer aprÚs coup.
- Toutes ses données (source Forge ou sprites propres au créateur, quel
  personnage/quelles animations) vivent dans une seule clé JSON
  _personnage_data (screens/rendering/personnage_data.py), sans aucune
  migration de schĂ©ma (mĂȘme patron que c_clause_list).
- Nouvelle échappatoire "custom_panel", symétrique à "special_render" mais
  pour le panneau de propriétés : ce widget affiche une galerie/un import
  de sprites sur-mesure plutÎt que les contrÎles génériques.

Galerie visuelle de personnages Forge (VRAIES miniatures, pas un emoji) :
- Panneau gauche "🎭 Personnages" : pose un personnage dĂ©jĂ  configurĂ©,
  animé immédiatement.
- Panneau droit (propriétés) : change le personnage Forge de l'élément
  sélectionné, ou importe les animations d'un sprite personnalisé.
- static/js/screen_edit/personnage-preview.js : lance l'aperçu animé de
  CHAQUE personnage du canevas dĂšs le chargement de la page et aprĂšs
  toute sauvegarde — le cƓur de la demande ("je dois voir ça bouger").

SĂ©lecteur d'animation Ă  miniatures (nƓud de flow "Jouer une animation" et
clip de Timeline "sprite") : remplace le menu déroulant/la zone de texte
par une grille de vraies miniatures, scopée aux SEULES animations du
personnage réellement ciblé (ELEMENT_ANIMATIONS_MAP, résolu cÎté serveur
Ă  partir des propriĂ©tĂ©s de cet Ă©lĂ©ment prĂ©cis) — jamais un catalogue
global. Le format stockĂ© sur le nƓud/le clip ({frames, fps, loop}) est
inchangé, seule l'UI d'édition change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 20:26:39 +02:00
williamandClaude Sonnet 5 d2c2f20b65 Phase 7 : personnage animé par sprites (poses/images successives)
Build and deploy / test-python (push) Successful in 1m26s
Build and deploy / test-js (push) Successful in 6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Ajoute une nouvelle action de flow "jouer_animation_sprite" qui joue une
séquence de PNG sur un élément image (cycle en boucle type marche, ou
une fois type saut) — rĂ©utilise target_element_id (dĂ©jĂ  whitelistĂ©) et
data_value en JSON {frames, fps, loop}, exactement le patron déjà établi
par "jouer_son" (Phase 6) et "alea" (Phase 2) : aucune nouvelle colonne,
aucune migration de schéma.

Ajoute une propriété d'élément "orientation" (Modifier un élément) pour
retourner un personnage en miroir (gauche/droite/bascule) sans nécessiter
une 2e feuille de sprites "vue de dos" — mĂȘme patron que "surbrillance"/
"désactivé".

Étend aussi la Timeline d'animation existante (kind="sprite", aux cĂŽtĂ©s
de "animate_css"/"custom") pour qu'un personnage puisse animer tout seul
dÚs l'affichage de l'écran (ex. idle en boucle perpétuelle), pas
seulement en rĂ©action Ă  un Ă©vĂ©nement — rĂ©utilise la colonne gĂ©nĂ©rique
custom_keyframes (JSON) et le réglage iteration_count déjà là pour la
boucle, aucune migration non plus. Les deux entrées (action de flow et
clip de timeline) partagent le mĂȘme moteur cĂŽtĂ© client
(runSpriteAnimation()/activeSpriteAnimations dans static/js/play/
actions.js, indexĂ© par nƓud DOM plutĂŽt que par id d'Ă©lĂ©ment pour
supporter plusieurs instances d'un mĂȘme Ă©cran-modĂšle animĂ©es
indépendamment).

Une bibliothĂšque de sprites Forge (2 personnages Kenney CC0, sous-
ensemble curé idle/marche/saut copié dans static/characters/) est
proposée dans l'éditeur, mais un créateur peut tout aussi bien utiliser
ses propres sprites uploadĂ©s (mĂȘme route d'upload gĂ©nĂ©rique que le son
de la Phase 6).

PérimÚtre volontairement limité à ce qui a été demandé : pas d'avatar
modulable en couches (assemblage cheveux/haut/bas par le joueur),
écarté du plan initial à la demande de l'utilisateur.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 19:18:07 +02:00
williamandClaude Sonnet 5 76331f9285 Phase 6 : son (musique de fond par écran + action "Jouer un son")
Build and deploy / test-python (push) Successful in 1m27s
Build and deploy / test-js (push) Successful in 6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Ajoute deux mécanismes distincts, à la demande de l'utilisateur qui a
prĂ©cisĂ© qu'un son doit pouvoir ĂȘtre attachĂ© Ă  une SCÈNE (pas seulement
joué ponctuellement par une action de flow, comme prévu initialement) :

- Musique de fond par écran (nouveau champ background_music_url sur
  _screens, ALTER TABLE nullable) : réglée dans le panneau gauche de
  l'éditeur (URL ou envoi de fichier, réutilise la route d'upload
  gĂ©nĂ©rique existante), enregistrĂ©e en AJAX au mĂȘme patron que le format
  d'aperçu (screen_set_aspect.py). Démarrée en boucle à l'affichage de
  l'Ă©cran et arrĂȘtĂ©e au changement d'Ă©cran (runScreenBackgroundMusic(),
  appelĂ©e depuis showScreen() dans static/js/play/screens.js) — un seul
  Audio actif à la fois, jamais cumulé avec une musique restée d'un écran
  précédent.

- Action de flow "jouer_son" (aux cÎtés des actions existantes) : effet
  sonore ponctuel, non bouclĂ©, dĂ©clenchable sur n'importe quel nƓud
  Déclencheur. Réutilise data_value (déjà un champ texte générique sur
  le nƓud action, comme pour "attendre") plutît qu'une nouvelle colonne
  dĂ©diĂ©e — fire-and-forget cĂŽtĂ© client (runActionNode), ne bloque jamais
  la suite du graphe.

Les deux rĂ©utilisent le mĂȘme mĂ©canisme d'upload de fichier dĂ©jĂ  en place
ailleurs dans l'éditeur (ex. source d'une vidéo), sans nouvelle route.

Ceci complÚte les 6 phases du plan d'extension du moteur (état par
joueur, hasard/maths, clavier/minuteur, ajout de ligne, position/
collision, son) : Forge Engine peut désormais couvrir des jeux bien
au-delĂ  du narratif/puzzle/quiz (action, arcade, jeux Ă  contrainte de
temps).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 17:56:49 +02:00
williamandClaude Sonnet 5 9e263435e6 Phase 5 : position/déplacement d'élément + condition de collision
Build and deploy / test-python (push) Successful in 1m29s
Build and deploy / test-js (push) Successful in 7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Ajoute le positionnement absolu (pos_x/pos_y, réutilise left/top en %
déjà en place) et relatif (pos_x_relatif/pos_y_relatif, ajoute un delta
à la position actuelle plutÎt que de l'écraser) comme nouvelles
propriétés de l'action "Modifier un élément".

Ajoute une nouvelle source de condition "collision" (aux cÎtés de
"objet"/"variable") : deux éléments (cond_element_a/cond_element_b,
ALTER TABLE sans contrainte FK, mĂȘme patron que block_id/
trigger_custom_event_id) dont on compare les rectangles à l'écran via
getBoundingClientRect() cÎté client (elementsOverlap(), dans
conditions.js). Pas d'opérateur/valeur à choisir : le chevauchement EST
directement le boolĂ©en vrai/faux du nƓud — le crĂ©ateur relie le port
"Faux" pour "ne se touchent pas", exactement comme pour n'importe quelle
autre condition (design plus simple que réinterpréter égal/différent,
qui n'a pas de sens pour superieur/inferieur).

delete_element.py et flow_nodes_referencing_element.py nettoient
dĂ©sormais aussi les nƓuds de collision rĂ©fĂ©rençant un Ă©lĂ©ment supprimĂ©
(ou l'un de ses descendants), pour rester cohérents avec le nettoyage
déjà en place pour trigger_element_id/target_element_id.

Combiné à la Phase 3 (minuteur récurrent) et au déplacement au clavier,
ça couvre des jeux type casse-briques/Pong/ramasse-objets sans
construire un vrai moteur physique (pas de vélocité/accélération/
gravité continues, cadrage volontairement limité).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 17:34:51 +02:00
williamandClaude Sonnet 5 92fbfbc9dd Phase 4 : ajout dynamique d'une ligne à l'exécution
Build and deploy / test-python (push) Successful in 1m26s
Build and deploy / test-js (push) Successful in 7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Nouveau sentinel LAST_INSERTED_ROW_ID = -3 (screens/flow/constants.py),
aux cĂŽtĂ©s de CLICKED_ROW_ID = -1 — mĂȘme principe : un id de ligne
n'existe qu'APRÈS l'insertion, jamais connu Ă  la crĂ©ation du nƓud.
Permet d'enchaĂźner un nƓud "Ajouter une ligne" (crĂ©e une ligne VIDE) puis
un ou plusieurs nƓuds "Modifier une donnĂ©e" dĂ©jĂ  existants (ciblant
"➕ DerniĂšre ligne ajoutĂ©e" dans le sĂ©lecteur "Ligne concernĂ©e", partagĂ©
avec les conditions) pour renseigner ses champs — rĂ©utilise 100% du
mécanisme actuel, aucun nouveau format de payload multi-champs.

screens/data_actions/apply_add_row_action.py : db.get_definition +
db.insert_row(slug, definition, {}, player_id) — la ligne appartient au
joueur qui agit pour un objet per_player (Phase 1).

Nouvelles routes (créateur ET publique, comme prévu dÚs la Phase 1) :
POST /game/<slug>/flow/nodes/<id>/run-add-row et son miroir
/jouer/<slug>/.../run-add-row — renvoient {"ok", "row_id"}.
routes/flow/flow_node_run_data.py (+ son miroir public) : résout aussi
LAST_INSERTED_ROW_ID (en plus de CLICKED_ROW_ID déjà en place) via
last_inserted_row_id transmis par le client.

static/js/play/actions.js : runActionNode branche "ajouter_ligne" ->
fetch la nouvelle route, pose window.lastInsertedRowId, puis
refreshRuntimeData() (un Répéteur lié affiche la nouvelle ligne au
prochain rendu, confirmĂ© par l'audit prĂ©alable — aucun ajustement du
mécanisme de rafraßchissement nécessaire). La branche "modifier_donnee"
transmet désormais aussi last_inserted_row_id, comme clicked_row_id.

templates/screen_edit.html + static/js/screen_edit/flow-editor.js :
nouveau type d'action "Ajouter une ligne Ă  un objet" (juste un
sĂ©lecteur d'objet, aucun champ Ă  remplir — le rappel du fonctionnement
enchaßné est affiché directement dans le formulaire) ; le sélecteur
"Ligne concernée" (partagé Condition/Modifier une donnée) gagne l'option
"➕ DerniĂšre ligne ajoutĂ©e" Ă  cĂŽtĂ© de "đŸ–±ïž Ligne cliquĂ©e".

Vérifié : 259 tests passent (4 nouveaux, dont un bout-en-bout via HTTP
qui enchaĂźne rĂ©ellement les deux nƓuds et vĂ©rifie le champ renseignĂ©, et
un qui verrouille l'isolation par joueur de la ligne créée), 13 tests
node:test toujours au vert, syntaxe JS validée.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 17:18:10 +02:00
williamandClaude Sonnet 5 727c3c97ba Phase 3 : déclencheur clavier + minuteur récurrent
Build and deploy / test-python (push) Successful in 1m28s
Build and deploy / test-js (push) Successful in 7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
screens/flow/constants.py : TRIGGER_EVENTS += "clavier" (À l'appui sur
une touche) et "minuteur" (Toutes les X millisecondes) — ni Ă©lĂ©ment ni
écran précis pour les deux, comme "evenement" déjà en place.
FLOW_NODE_FIELDS += trigger_key/trigger_interval_ms. ensure_flow_schema.py :
ALTER TABLE pour les 2 colonnes (patron trigger_custom_event_id).

templates/screen_edit.html + static/js/screen_edit/flow-editor.js :
- "clavier" : un champ "Touche Ă  surveiller" qui capture lui-mĂȘme la
  touche pressée (onkeydown sur l'input, captureFlowTriggerKey()) plutÎt
  que de faire deviner la syntaxe attendue (ev.key du navigateur, ex.
  "ArrowUp", "a", " " pour Espace).
- "minuteur" : un simple champ numérique (millisecondes).
- nodeLabel() affiche "⌚ Touche « X »"/"⏱ Toutes les N ms" sur le nƓud.

static/js/play/triggers.js (moteur de jeu) :
- bindKeyboardTriggers() : UN SEUL window.addEventListener('keydown', ...)
  posé une fois pour tout le jeu (voir l'amorçage en fin de
  templates/play.html) — mĂȘme patron de scan global que
  dispatchGameEvent() pour "Sur un événement personnalisé".
- runScreenTimerTriggers(screenId) : gĂ©rĂ© PAR ÉCRAN (appelĂ© depuis
  showScreen(), static/js/play/screens.js) — dĂ©marre les setInterval des
  nƓuds "minuteur" de l'Ă©cran affichĂ©, arrĂȘte d'abord tous ceux de
  l'affichage prĂ©cĂ©dent (mĂȘme principe que runAnimationTimeline) pour
  ne jamais accumuler des minuteurs sur des écrans quittés.

Vérifié : 255 tests passent (5 nouveaux, dont un qui verrouille que la
touche Espace — trĂšs probablement utilisĂ©e en jeu — n'est pas filtrĂ©e
comme une valeur "vide" par add_flow_node.py), 13 tests node:test
toujours au vert, syntaxe JS validée sur tous les fichiers de
static/js/play/ et static/js/screen_edit/. Comme le reste du graphe de
logique cĂŽtĂ© client, le comportement RÉEL d'un keydown/setInterval n'est
pas testable sans navigateur — test manuel recommandĂ© (touche assignĂ©e
Ă  un saut, minuteur faisant avancer un compteur).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 17:05:55 +02:00
williamandClaude Sonnet 5 7476ed229e Phase 2 : hasard + opérations mathématiques
Build and deploy / test-python (push) Successful in 1m26s
Build and deploy / test-js (push) Successful in 6s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
screens/labels/data_operations.py : 6 nouvelles opérations pour
"Modifier une donnĂ©e"/"Modifier une variable" — multiplier, diviser,
modulo (garde-fou division par zéro : valeur inchangée plutÎt qu'une
ZeroDivisionError qui interromprait le graphe), minimum/maximum (borne
la valeur ACTUELLE — utile pour une variable globale, qui n'a pas de
min_value/max_value comme un champ d'objet), et alea (tire un nombre
aléatoire entre deux bornes).

screens/data_actions/compute_operation.py (déjà factorisé en Phase 0,
donc une seule implémentation pour apply_data_action.py/
apply_variable_action.py) : implémente les 6. "alea" est la seule à
deux opĂ©randes — rĂ©utilise data_value au format "min,max" plutĂŽt qu'une
nouvelle colonne de nƓud (bornes remises dans l'ordre si inversĂ©es).
random.uniform pour un résultat décimal, random.randint pour un entier.

static/js/screen_edit/flow-editor.js + templates/screen_edit.html :
petit indice visuel — le champ "Valeur / montant" du formulaire de nƓud
affiche "min,max (ex. 1,6)" quand "alea" est choisi, pour ne pas laisser
deviner ce format à deux nombres, différent de toutes les autres
opérations. Sinon aucun nouveau champ/changement de schéma nécessaire,
le <select> était déjà généré depuis DATA_OPERATIONS.

Vérifié : 250 tests passent (19 dans test_compute_operation.py, dont un
qui a dĂ» ĂȘtre corrigĂ© — il utilisait "multiplier" comme exemple
d'opération INCONNUE, devenu un mauvais exemple maintenant qu'elle
existe), 13 tests node:test toujours au vert, syntaxe JS validée.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 16:47:59 +02:00
williamandClaude Sonnet 5 fe807ba51e Phase -1 (suite) : découpe l'éditeur de screen_edit.html en modules JS
MĂȘme chantier que le commit prĂ©cĂ©dent (moteur de jeu, play.html) —
templates/screen_edit.html était un unique fichier HTML+CSS+JS de 3312
lignes, tout l'éditeur (arborescence, panneaux flottants, canevas,
formulaire de propriĂ©tĂ©s, Ă©diteur de flow Ă  nƓuds, blocs de logique,
timeline d'animation) vivant dans UN SEUL <script>.

Ce fichier est plus imbriqué que play.html : de nombreux appels
s'exécutent au niveau racine du script (pas seulement des déclarations
de fonctions), et JavaScript hoiste les déclarations `function` sur
TOUT le script — un appel au niveau racine peut donc rĂ©fĂ©rencer une
fonction dĂ©clarĂ©e PLUS LOIN dans le mĂȘme fichier. DĂ©couper naĂŻvement
casserait cet ordre implicite. Un audit dédié (analyse ligne par ligne
de chaque appel racine + son graphe d'appel transitif) a identifié 3
rĂ©fĂ©rences "en avance" rĂ©elles, toutes regroupĂ©es dans la mĂȘme zone
(initBuilderPanel()/toggleActionFields() → bindAspectButtons/
toggleElementPropertyValue/onDataDefinitionChange) — le dĂ©coupage
respecte cette contrainte : chaque fichier est une TRANCHE SÉQUENTIELLE
de l'original (jamais une réorganisation), et cette zone spécifique
reste un seul fichier (panel-init.js) pour que le hoisting continue de
fonctionner exactement comme avant.

5 fichiers sous static/js/screen_edit/ :
- tree-panels.js — arborescence, menu contextuel, panneaux flottants
  gauche/droite, galerie d'icĂŽnes, modale de suppression/choix d'icĂŽne,
  glisser-déposer du canevas, panneau de propriétés (autosave).
- panel-init.js — (rĂ©)initialisation du panneau central aprĂšs chaque
  changement de sélection, filtres de répéteur/donnée liée, condition
  de visibilitĂ©, champs d'action du formulaire de nƓud.
- flow-editor.js — Ă©diteur de flow Ă  nƓuds (rendu du graphe, formulaire
  d'ajout de nƓud, blocs de logique — currentBlockNodes/Edges).
- tabs-and-blocks.js — onglets du centre, panneaux flottants gĂ©nĂ©riques
  (drag/resize/plein écran), modale d'un bloc de logique.
- animation-timeline.js — timeline d'animation (clips Animate.css/
  personnalisés).

Toutes les données injectées par Jinja (GAME_SLUG, SCREEN_ID,
DEFINITIONS_DATA, FLOW_NODES_INITIAL, ELEMENTS_LABELS, CUSTOM_EVENTS_MAP,
ANIM_CLIPS...) sont posées UNE FOIS par un petit <script> inline resté
dans le template, avant les <script src> — mĂȘme patron que
static/js/play/. Le seul bout de logique resté inline est la toute
petite IIFE d'ouverture initiale (?tab=/?block=), qui dépend directement
de request.args et doit s'exécuter aprÚs que tous les fichiers soient
chargés.

tests/conftest.py : screen_edit_js_bundle() (mĂȘme principe que
play_js_bundle(), Phase -1 prĂ©cĂ©dente) — 4 tests qui vĂ©rifiaient la
présence de telle fonction/chaßne dans le HTML de l'éditeur (le JS y
était inline) sont mis à jour pour chercher dans ce bundle. Un des deux
échecs révélait un test déjà fragile (assert "Ligne cliquée" in html
vérifiait en réalité le TEXTE SOURCE d'un <script> inline, jamais du
HTML rĂ©ellement rendu — ce texte ne peut plus s'y trouver une fois la
fonction qui le construit dynamiquement déplacée dans un fichier
externe) : corrigé pour vérifier le bundle JS + la disponibilité de la
route séparément.

Vérifié : 215 tests passent, syntaxe JS validée sur les 5 nouveaux
fichiers (node --check) et sur les <script> inline restants (rendus via
le client de test). Test manuel recommandé (édition complÚte d'une
scÚne : arborescence, propriétés, glisser-déposer, logique de flow,
blocs, timeline) avant de considérer ce découpage définitivement sans
risque — comme pour play.html, ce fichier n'a pas de harnais de test
DOM automatisé.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 14:38:57 +02:00
williamandClaude Sonnet 5 dbada333d5 Phase -1 : découpe le moteur de play.html en modules JS + premiers tests JS
templates/play.html était un unique fichier HTML+CSS+JS de 1069 lignes,
tout le moteur de jeu vivant dans UN SEUL <script>, sans aucune
couverture de test sur cette logique (seuls le rendu HTML et la syntaxe
JS étaient vérifiés). La feuille de route à venir (état par joueur,
hasard, clavier/minuteur, position/collision, son — voir le plan) va
justement faire grossir ce moteur : "un fichier = une fonction, un
dossier = une responsabilité" s'applique aussi au JS, pas seulement au
Python — le moment de dĂ©couper est avant d'ajouter encore plus de code,
pas aprĂšs.

Découpage en 6 fichiers sous static/js/play/, calqués sur les sections
déjà présentes dans le code (aucune réorganisation de logique, une pure
extraction) : screens.js (affichage d'écran, timeline d'animation),
conditions.js (Ă©valuation des conditions — la partie 100% PURE, sans
DOM, la plus testable), actions.js (exécution des actions), triggers.js
(recherche des nƓuds dĂ©clencheurs, attache des Ă©couteurs), bindings.js
(résolution des {{champ}}, rafraßchissement des données), flow-engine.js
(parcours du graphe, événements personnalisés).

Zéro nouvel outillage : plusieurs <script src> dans l'ordre, partageant
le mĂȘme espace global qu'avant (aucun bundler, aucune Ă©tape de build).
Les 2 URLs de route dont ces fichiers ont besoin (flow_node_run_data/
run_variable, runtime_payload) ne peuvent plus ĂȘtre injectĂ©es par Jinja
directement dans le code (un fichier statique n'est jamais passé par le
moteur de templates) — elles sont maintenant posĂ©es une fois dans
window.FORGE_PLAY_URLS par le petit <script> inline restant dans
play.html, qui ne porte plus que les données Jinja (gameData) et
l'amorçage (bindClicks() etc. au chargement).

publish/build_package.py : ajoute static/js/play Ă  la liste des fichiers
copiés dans l'exécutable exporté (le mode jouable en dépend désormais).

Premiers tests JS (static/js/play/__tests__/conditions.test.js, lancés
via `node --test`, zĂ©ro nouvelle dĂ©pendance npm — decision prise avec
l'utilisateur de commencer par la logique PURE seulement, pas par une
couverture DOM via jsdom) : compareValues, resolveVariablePath,
evaluateConditionClause/Node, exactement la logique que les phases Ă 
venir (opérations mathématiques, condition de collision) vont étendre.

tests/conftest.py : nouveau helper play_js_bundle() (concatĂšne tout
static/js/play/*.js) — 13 tests existants qui vĂ©rifiaient la prĂ©sence de
telle fonction/chaĂźne dans le HTML de /game/<slug>/play (tout le JS y
était inline avant ce découpage) sont mis à jour pour chercher dans ce
bundle à la place ; les tests qui vérifient un CSS/HTML réellement resté
dans play.html (forgeHighlight, forgeDisabled, #playFrame...) continuent
de chercher dans le HTML.

Vérifié : 215 tests pytest passent (aucune régression comportementale,
juste une réorganisation), 13 tests node:test passent, node --check sur
chacun des 6 nouveaux fichiers. Test manuel recommandé (jeu joué de bout
en bout : navigation, clic, survol, répéteur, condition, animation)
avant de considérer le découpage définitivement sans risque.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 13:59:11 +02:00