Commit Graph
16 Commits
Author SHA1 Message Date
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
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 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 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 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