Deux fonctionnalités demandées, développées et corrigées dans cet
échange :
1. "Donnée liée" (Texte/Titre) : le réglage à 2 filtres fixes (toujours
combinés en ET) devient une liste de conditions ILLIMITÉE, avec un
choix ET/OU pour les combiner (screens/widgets/controls/c_clause_list.py,
screens/clause_list_codec.py). Rétrocompatible avec les anciens
éléments (_data_filtre_champ/_data_filtre2_champ), convertis à la
volée à la lecture, sans migration. Après un premier essai à la
présentation trop compacte et technique (retour utilisateur : "pas de
champ technique, pas de notation bizarre {{ }}"), la présentation
finale reprend EXACTEMENT l'ancien style (labels "Champ"/"...est"/
"...cette valeur", même sélecteur de valeur fixe/dynamique/variable
déjà existant, jamais la syntaxe brute), simplement répétée par
condition (templates/partials/clause_row.html), avec un bouton
"+ Ajouter une condition" bien visible et une liste scrollable
(static/style.css, .clauseListWrap). Le même moteur (filter_repeater_
rows.py généralisé) profite aussi au Répéteur de données en interne.
2. Nœud Condition de la Logique de la scène : peut désormais tester une
VARIABLE GLOBALE en plus d'un champ d'objet (cond_source/cond_variable/
cond_variable_chemin — screens/flow/ensure_flow_schema.py), sur la
clause principale ET chaque clause supplémentaire (ET/OU). Évalué côté
CLIENT (templates/play.html, evaluateConditionClause), contre un
nouveau gameData.variables exposé par full_game_payload.py — tenu à
jour par refreshRuntimeData() après toute action qui modifie une
variable, sans changement supplémentaire nécessaire. Le panneau de
condition reste utilisable même sans aucun objet défini dans le jeu
(avant, il disparaissait entièrement).
Vérifié : 153 tests pytest (nouveaux : test_data_binding_clause_list.py,
test_condition_variable.py) + logique JS d'évaluation des conditions
vérifiée isolément avec Node (variable scalaire, objet avec chemin
chaîné, tableau par index, variable introuvable, booléen, rétrocompatibilité
legacy) + rendu des deux pages (éditeur/jeu) vérifié sur le vrai projet
"test" en plus des jeux de test.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Deux retours après le dernier correctif (78a373a, qui forçait
"display:flex" dans l'éditeur pour que la boîte de dialogue reste
visible) :
- "je souhaite avoir la main sur la visibilité de la modale sinon elle
s'affiche toujours sur la scène, court-circuite ma logique" — le
forçage empêchait de vraiment utiliser "Visibilité" pendant l'édition.
- "quand j'édite la modale ou quand je la mets dans une scène je
souhaite que rien ne soit assombri, l'assombrissement ne se fait que
quand la scène est jouée" — le voile plein écran (position:fixed +
fond assombri) restait aussi actif dans l'éditeur.
Correctif (render_overlay.py) : le voile plein écran ET le forçage de
visibilité sont retirés de l'ÉDITEUR — ce widget s'y comporte maintenant
comme un CONTENEUR NORMAL (position/taille selon x/y/width/height, aucun
voile, réglage "Visibilité" respecté normalement, comme n'importe quel
autre widget masqué). Le comportement plein écran/voile/masquage par
défaut n'est conservé qu'en mode JOUABLE (ctx["_forge_play_mode"]).
En creusant pourquoi "Visible" ne suffisait pas à faire réapparaître la
boîte dans l'éditeur (menant l'utilisateur à essayer "Invisible" à la
place, visible dans ses captures), trouvé un vrai bug latent dans
save_element_controls.py : l'option "Visible" du réglage "Visibilité" ne
touche volontairement jamais "display" (pour ne pas écraser le
"display:flex" d'un conteneur en disposition ligne/colonne — voir
visibility_control.py) — ça fonctionne seulement parce que, pour un
widget AVEC un réglage "Disposition interne", celui-ci réaffirme lui-même
un display non-"none" au même enregistrement. La "superposition" n'a PAS
ce réglage : "display:none" (posé à la création ou par un "Masqué"
précédent) restait donc bloqué pour toujours, quel que soit le nombre de
fois où "Visible" était ensuite choisi. Corrigé : "Visible" efface aussi
"display" pour tout widget SANS réglage "Disposition interne" (safe : les
widgets qui EN ont un ne sont pas concernés, donc aucune régression sur
leur comportement existant).
Tests mis à jour (l'ancien test attendait le forçage, désormais retiré) +
nouveau test qui couvre le cycle complet (masqué par défaut -> "Visible"
choisi -> apparaît sans voile dans l'éditeur -> voile plein écran
retrouvé en mode jouable). 140 tests au vert au total.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bug rapporté : poser un élément de jeu ("Mes éléments de jeu") dont le
modèle n'est qu'une "Superposition / boîte de dialogue" sur une vraie
scène affichait un conteneur vide à l'endroit du dépôt — jamais le
dialogue. La boîte de dialogue existait bien (masquée comme prévu, en
attente d'une action qui l'affiche) : c'est l'enveloppe qui l'entoure qui
n'aurait jamais dû être visible.
Cause : tout exemplaire d'élément de jeu est posé avec le widget générique
"conteneur" par défaut (add_element.py, colonne default_widget) — son
contenu réel (le modèle) est rechargé EN DIRECT à l'intérieur
(_render_element_type_children), mais l'enveloppe "conteneur" elle-même
reste une boîte NORMALE, toujours visible, avec sa propre couleur de fond/
bordure et sa position fixe sur le canevas (contrairement à une
superposition posée directement, qui, elle, démarre masquée). Résultat :
une boîte vide et permanente à l'endroit du dépôt, pendant que le vrai
dialogue (démarré masqué, correctement) reste invisible en dessous/
au-dessus tant qu'aucune action ne le déclenche.
Correctif : quand le modèle ENTIER d'un élément de jeu n'est qu'une seule
superposition (screens/element_types/is_overlay_only.py, nouveau), on
court-circuite entièrement l'enveloppe "conteneur" (render_element_html.py)
et on retire aussi le z-index de son cadre de positionnement
(element_style_filter.py, list_elements.py) — même raison que pour une
superposition posée directement (81c31a9) : sans ça, ce cadre reste un
candidat à piéger le z-index:9999 du dialogue rendu à l'intérieur dès
qu'un autre élément de la scène a un z-index plus grand.
Nouveau test, confirmé en échec sur l'ancien code (même "class=\"box\""
fantôme reproduit) puis au vert avec le correctif. 140 tests au vert au
total.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Après vérification serveur, le texte était bel et bien rendu dans le HTML
(couleur correcte incluse) — donc pas un souci de contenu ni de couleur.
La vraie cause : le widget "Superposition / boîte de dialogue" démarre
MASQUÉ par défaut à sa création (display:none, voir
default_style_for_widget.py — pour ne pas couvrir tout l'écran dès qu'on
le pose). Ce display:none est écrit tel quel dans le HTML aussi bien en
mode jouable QUE dans l'éditeur, puisque le rendu de ce widget est
partagé par les deux. Résultat : toute la boîte (et donc son contenu,
peu importe le texte ou sa couleur) restait invisible dans le CANEVAS DE
L'ÉDITEUR dès l'instant de sa création — impossible d'y voir/positionner
visuellement ce qu'on pose dedans tant qu'on n'a pas pensé à basculer
manuellement "Visibilité" sur "Visible" (puis à y repenser pour la
remettre sur "Masqué" avant de tester en jeu).
Correctif (render_overlay.py) : dans l'ÉDITEUR uniquement (détecté via le
ctx "_forge_play_mode" déjà posé par list_elements.py pour le mode
jouable), un "display:flex" est ajouté en dernier dans le style —
gagnant sur le "display:none" par défaut (CSS : la dernière déclaration
de la même propriété l'emporte). Le mode JOUABLE, lui, continue de
respecter ce réglage normalement (masqué tant qu'aucune action ne
l'affiche).
Nouveau test, confirmé en échec sur l'ancien code puis au vert avec le
correctif : vérifie explicitement que le style se termine par
"display:flex;" dans l'éditeur et par "display:none;" en mode jouable.
139 tests au vert au total. Jeu de démo régénéré.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Régression du commit précédent (d87d6ad, classes Bulma sur la
superposition) : la classe ".box" impose elle-même une couleur de texte
SOMBRE (pensée pour un fond blanc). Comme aucun widget ne fige de couleur
de texte à sa création (default_style_for_widget.py), un titre/texte posé
dans la boîte SANS couleur personnalisée héritait de ce gris sombre
imposé par Bulma — invisible sur le fond sombre par défaut de cette boîte
de dialogue. Ni erreur serveur ni régression de test visible : juste un
texte "présent mais invisible", exactement ce qui a été rapporté ("j'ai
mis un texte dedans mais il ne se voit pas").
Correctif : la boîte fixe elle-même une couleur de texte claire par
défaut (color:#e8eaf0) dans son propre style inline — l'élément le plus
proche gagne, donc ça écrase la couleur imposée par la classe Bulma, tout
en restant surchargeable : un titre/texte qui personnalise sa propre
couleur (réglage "Couleur du texte") continue de l'emporter normalement.
Nouveau test de régression, confirmé en échec sur le commit précédent
(même style Bulma, pas encore cette couleur) puis au vert avec le
correctif. 138 tests au vert au total. Jeu de démo régénéré.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le correctif précédent (81c31a9) retirait TOUTE position/taille du cadre
.canvasElement/.playElement d'une "superposition" pour ne plus piéger son
propre z-index:9999 dans un contexte d'empilement local — mais ce même
cadre sert aussi, dans l'ÉDITEUR, de prise pour le glisser-déposer/
redimensionnement de ce widget (screen_edit.html). Sans
position:absolute + left/top/width/height, ce cadre s'effondre à 0×0 (son
contenu réel est en position:fixed, hors flux) : le calcul de geometrie
divise alors par une dimension nulle, produit NaN, et
JSON.stringify(NaN) envoie littéralement "null" au serveur — qui plantait
sur float(None) dans routes/elements/element_geometry.py (500, reproduit
par l'utilisateur en posant un template lié à un objet et en essayant de
repositionner l'overlay dans l'éditeur).
Correctif plus ciblé : le cadre garde position/left/top/width/height comme
n'importe quel widget (l'éditeur redevient fonctionnel) — SEUL le z-index
est omis pour ce widget précis. position:absolute SANS z-index explicite
(donc z-index:auto) ne crée PAS de nouveau contexte d'empilement CSS :
le z-index:9999 posé plus profond (render_overlay.py) continue donc de se
comparer directement aux autres éléments de l'écran, et gagne toujours —
le bug de superposition visuelle (81c31a9) reste corrigé, sans regression
sur l'éditeur cette fois.
test_confort.py mis à jour (position:absolute attendu, plus d'assertion
"position:static") + nouveau test qui appelle directement la route
/geometry sur une superposition pour confirmer qu'elle répond 200.
137 tests au vert au total.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bug visible sur le jeu de démo : le bouton "Clique-moi !" restait visible
ET cliquable AU-DESSUS du dialogue de bienvenue censé couvrir tout l'écran,
et la boîte de dialogue elle-même s'étirait bord à bord au lieu de rester
une boîte centrée lisible.
Cause (stacking context CSS) : le widget "superposition" ignore x/y/
width/height et pose lui-même position:fixed; inset:0; z-index:9999 sur
SA PROPRE balise (render_overlay.py) — mais le cadre .playElement/
.canvasElement qui l'entoure, PARTAGÉ PAR TOUS LES WIDGETS (filters/
element_style_filter.py), continuait quand même à poser
"position:absolute; z-index:<sa place dans le canevas>" (souvent petit,
ex. 1). Un élément positionné avec un z-index explicite crée un NOUVEAU
contexte d'empilement CSS : le 9999 posé plus profond ne se comparait
alors plus qu'AU SEIN de ce contexte, et perdait face au z-index (plus
grand) d'un élément ajouté APRÈS l'overlay sur le canevas — qui
s'affichait donc par-dessus le dialogue.
Correctif : _element_style ne pose plus aucune position/z-index pour ce
widget (position:static — sa place dans le flux est de toute façon
invisible, son contenu réel étant en position:fixed). Plus de contexte
d'empilement local créé à ce niveau : le z-index:9999 se compare
directement à tous les autres éléments de l'écran, et gagne toujours.
Profité de l'occasion pour donner à la boîte une largeur par défaut plus
raisonnable (render_overlay.py : max-width:min(560px, 90%) au lieu de
90% seul) — sur un écran de jeu large, "90%" donnait une boîte étirée
bord à bord peu lisible comme dialogue ; 560px reste confortable, et 90%
prend toujours le relais sur un écran étroit (mobile/portrait).
Nouveau test de régression (test_overlay_wrapper_does_not_trap_its_own_z_index) :
confirmé en échec sur l'ancien code (git stash), au vert avec le
correctif. 129 tests au vert au total.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add "survol"/"fin_survol" trigger events (mouseenter/mouseleave,
same anti-doublon pattern as bindClicks()) so hovering an element can
run flow nodes, instead of the old static hover-text-only control
removed from the properties panel. Also add "Contenu" as a settable
"Modifier un élément" property so an action can display a defined
text on another element on hover — the concrete use case that
motivated this.
Verified live with Playwright: a "Au survol" trigger on one element
correctly updates another element's text via the action, and text
stays put with no "Fin du survol" wired (explicit, no implicit
revert, consistent with "Au clic").
- Merge "Taille dans le conteneur" into "Position & taille": a child
element (posé dans un conteneur/répéteur/groupe de champs) now shows
greyed-out X/Y (not applicable, it follows its parent's layout) and
a single Largeur/Hauteur field per axis with a unit selector (%/px)
instead of the old fixed-px-only sliders capped at 1000 — which was
the root cause of a landscape-mode bug where a child couldn't be
made wider than 1000px even though the actual screen was wider.
New c_size control type (one style key per axis, not two competing
sliders — an earlier px+% two-slider attempt let the untouched
slider silently clobber the other's value on every autosave).
- Merge the two "Disposition" groups (visibility + scale, previously
split apart in UNIVERSAL_CONTROLS by unrelated groups) into one.
- Remove the "Survol" panel: hovering is conceptually a flow trigger,
not a static element property — to be reintroduced there. The
underlying data-hover-text/bindHoverTexts runtime is untouched.