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.
This commit is contained in:
william
2026-09-04 20:56:32 +02:00
parent 00f7191a8b
commit 50835a18e2
221 changed files with 1239 additions and 12245 deletions
+119 -13
View File
@@ -219,10 +219,21 @@ input[type=checkbox]{ width:auto; margin-top:6px; }
les différents éditeurs d'un même jeu, en ligne (contrairement à
.navList/.navRow ci-dessous, empilés verticalement dans le panneau
"Créer" du tableau de bord). */
.gameNavBar{ display:flex; flex-wrap:wrap; justify-content:center; gap:6px; padding:8px 20px; background:transparent; border-bottom:1px solid var(--border); }
/* space-between (jamais center, voir le commentaire d'en-tête plus haut) :
"Mes écrans" (page de gestion) reste à gauche, "Jouer"/"Exporter"
(.gameNavRight, des ACTIONS) groupés à droite — demande explicite. */
.gameNavBar{ display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center; gap:6px; padding:8px 20px; background:transparent; border-bottom:1px solid var(--border); }
.gameNavRight{ display:flex; flex-wrap:wrap; gap:6px; margin-left:auto; }
.gameNavLink{ display:inline-flex; align-items:center; gap:6px; padding:6px 10px; border:1px solid var(--border); border-radius:6px; background:var(--panel); text-decoration:none; color:var(--text); font-size:12.5px; }
.gameNavLink:hover{ background:var(--panel2); border-color:var(--accent); }
.gameNavLink.active{ background:var(--panel2); border-color:var(--accent); color:var(--accent); }
/* "Jouer" (orange, --accent) / "Exporter" (vert, --status-success) —
couleur pleine plutôt qu'un simple contour, pour ressortir comme des
actions parmi les liens de gestion neutres. */
.gameNavLink--play{ background:var(--accent); border-color:var(--accent); color:#fff; font-weight:600; }
.gameNavLink--play:hover{ background:var(--accent); filter:brightness(1.1); border-color:var(--accent); }
.gameNavLink--export{ background:var(--status-success); border-color:var(--status-success); color:#fff; font-weight:600; }
.gameNavLink--export:hover{ background:var(--status-success); filter:brightness(1.1); border-color:var(--status-success); }
.content{ max-width:760px; margin:0 auto; padding:28px 20px; }
.card{ border:1px solid var(--border); border-radius:10px; padding:16px; background:var(--panel); margin:12px 0; }
@@ -246,14 +257,26 @@ input[type=checkbox]{ width:auto; margin-top:6px; }
.onboardingStep{ flex:0 0 50%; width:50%; box-sizing:border-box; padding:12px 24px; }
/* ---------- Étape 1 : cartes de type, grandes, retournables ---------- */
.onboardingGrid{ display:flex; gap:18px; height:66vh; min-height:420px; max-height:640px; }
/* Un seul choix aujourd'hui (RPG, voir db/games/game_type_catalog.py) :
pleine hauteur (comme avant), mais LARGEUR d'1/4 (comme s'il y avait
toujours 4 cartes côte à côte) et centrée — demande explicite, plutôt
que de repenser toute la mise en page pour un seul choix. */
.onboardingGrid{ display:flex; gap:18px; height:66vh; min-height:420px; max-height:640px; max-width:25%; margin:0 auto; transition:max-width .35s ease; }
/* Élargit la carte (jamais un scale() : demande explicite, "s'élargir sur
sa width", pas grossir dans les deux sens) au survol/à la sélection —
pour lire confortablement le dos retourné (titre + liste + badge, voir
.onboardingPitch*) sans reprendre toute la mise en page à 4 cartes. Le
sélecteur :has() cible le PARENT depuis l'état d'un enfant (support
navigateurs modernes déjà requis ailleurs, voir container queries
"cqw" plus bas). */
.onboardingGrid:has(.onboardingCard:hover), .onboardingGrid:has(.onboardingCard.is-selected){ max-width:42%; }
.onboardingCard{
flex:1; position:relative; cursor:pointer; padding:0; margin:0; border:none; background:none;
border-radius:20px; perspective:1400px;
opacity:0; animation:forgeOnboardCardIn .5s ease both;
transition:flex-grow .3s ease;
}
.onboardingCard:hover{ flex-grow:1.25; }
.onboardingCard:hover{ flex-grow:1.25; z-index:1; }
.onboardingCardInner{
position:relative; display:block; width:100%; height:100%;
transform-style:preserve-3d; transition:transform .6s cubic-bezier(.4,.2,.2,1);
@@ -269,9 +292,30 @@ input[type=checkbox]{ width:auto; margin-top:6px; }
.onboardingCardIcon{ font-size:3.4em; line-height:1; filter:drop-shadow(0 4px 10px rgba(0,0,0,.35)); }
.onboardingCardLabel{ font-weight:800; font-size:1.3em; letter-spacing:.01em; }
.onboardingCardBack{
transform:rotateY(180deg); background:var(--panel); border:2px solid var(--accent);
transform:rotateY(180deg); background:linear-gradient(165deg, var(--panel), var(--panel2));
border:2px solid var(--accent); justify-content:flex-start; padding:26px 22px;
}
/* Argumentaire de vente (voir db/games/game_type_catalog.py::ONBOARDING_TYPES,
"description" contient du HTML de confiance — jamais de saisie
utilisateur — d'où |safe dans le template) : un titre accrocheur, une
liste à coche plutôt qu'un bloc de texte brut, un badge LMS mis en
avant en bas — pensé pour convaincre un public RH non technique en un
coup d'œil, pas juste informer. */
.onboardingCardDesc{ display:flex; flex-direction:column; gap:16px; width:100%; text-align:left; }
.onboardingPitchTitle{ font-size:1.15em; font-weight:800; line-height:1.3; color:var(--text); }
.onboardingPitchList{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:12px; }
.onboardingPitchList li{ display:flex; align-items:flex-start; gap:10px; font-size:.93em; line-height:1.45; color:var(--text-dim); }
.onboardingPitchCheck{
flex:0 0 auto; width:20px; height:20px; border-radius:50%; background:var(--gradient);
color:#fff; display:flex; align-items:center; justify-content:center; font-size:.7em; font-weight:900;
box-shadow:0 2px 8px var(--glow); margin-top:1px;
}
.onboardingPitchList strong{ color:var(--text); }
.onboardingPitchBadge{
display:flex; align-items:center; gap:8px; font-size:.85em; font-weight:700;
color:var(--accent); background:rgba(255,95,46,.12); border:1px solid rgba(255,95,46,.35);
border-radius:999px; padding:8px 14px; align-self:flex-start;
}
.onboardingCardDesc{ color:var(--text); font-size:1em; line-height:1.5; }
/* Une couleur de dégradé par type — voir data-type dans le template,
généré depuis db.ONBOARDING_TYPES (mêmes clés que ci-dessous). */
@@ -513,10 +557,14 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
}
.floatPanelHeader .floatPanelTitle{ font-size:11px; color:var(--text-dim); text-transform:uppercase; letter-spacing:.05em; }
.floatPanelBody{ padding:12px; overflow-y:auto; min-height:0; flex:1 1 auto; }
.floatPanelReopenBtn{ position:fixed; top:90px; z-index:60; display:none; }
/* Posés dans la barre d'outils de la scène (templates/scene_edit.html),
plus en position:fixed flottant au-dessus de toute la page — ils
chevauchaient "Mes écrans"/"Jouer"/"Exporter" (voir base.html) une fois
les panneaux fermés. display:none/.visible (JS : openFloatPanel/
closeFloatPanel, static/js/screen_edit/tree-panels.js) reste la même
logique : visible seulement quand le panneau correspondant est fermé. */
.floatPanelReopenBtn{ display:none; }
.floatPanelReopenBtn.visible{ display:inline-flex; }
#panelLeftReopenBtn{ left:24px; }
#propsPanelReopenBtn{ right:24px; }
.widgetGrid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
@@ -615,6 +663,29 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
.canvasElement:hover{ border-color:rgba(91,140,255,.5); }
.canvasElement.selected{ border:2px solid var(--accent); }
.canvasElementInner{ width:100%; height:100%; pointer-events:none; }
/* render_scene_object.py positionne son <img> en absolu (left/top/width/
height en px) pour s'afficher correctement en enfant DIRECT de
.playScreen.playScene (voir templates/play.html) — dans le canevas de
l'éditeur de scène (templates/scene_edit.html), cette même balise est
nichée dans .canvasElementInner, lui-même déjà positionné/dimensionné
par .canvasElement ci-dessus : sans ce correctif, l'image se
positionnait EN PLUS depuis #canvas (son ancêtre positionné le plus
proche), en double du décalage déjà appliqué par .canvasElement.
Posé ici (feuille de style globale, chargée par TOUTES les pages) et
non plus dans un <style> inline propre à scene_edit.html : la
navigation "Mes écrans" -> scène se fait en pjax (voir static/pjax.js),
qui ne fusionne dans <head> que les <link rel="stylesheet"> manquants,
JAMAIS un <style> inline — arriver sur une scène par un clic (plutôt
qu'un vrai chargement de page) laissait donc cette règle absente, et
les personnages/décors se retrouvaient mal positionnés (invisibles)
tant qu'aucun rechargement complet n'avait eu lieu. */
.canvasElementInner > img{ position:static !important; left:auto !important; top:auto !important; width:100% !important; height:100% !important; z-index:auto !important; }
/* Même correctif que ci-dessus, pour les widgets d'interface ("💬 Boîte
de dialogue"/"❓ Boîte à quiz"/"🏆 Score", screens/scenes/
render_scene_object.py) — des <div>, pas des <img>. */
.canvasElementInner > .dialogueBoxWidget, .canvasElementInner > .quizBoxWidget, .canvasElementInner > .scoreWidget{
position:static !important; left:auto !important; top:auto !important; width:100% !important; height:100% !important; z-index:auto !important;
}
/* Objet de scène (screens/scenes/render_scene_object.py, éditeur ET jeu
jouable) : object-fit:contain — les frames CraftPix (screens/labels/
animal_sprite_library.py) sont de grands canevas très paddés, très
@@ -808,19 +879,31 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
border:1px solid var(--border); border-radius:12px; padding:8px 10px;
}
.collisionRuleChainLinks{ display:flex; align-items:center; flex-wrap:wrap; row-gap:6px; }
/* Pointe/encoche en PIXELS FIXES (calc(100% - 14px), jamais un pourcentage
de la largeur du maillon) — un maillon plus court ("PNJ") ou plus long
("Déclencher une quête « ... »") a alors TOUJOURS la même profondeur de
pointe, qui s'emboîte exactement dans l'encoche du suivant (même 14px
que la marge négative ci-dessous) : avec un pourcentage, la profondeur
de la pointe variait avec la largeur du texte, sans jamais coïncider
avec l'encoche du voisin — les pièces flottaient les unes par rapport
aux autres au lieu de s'emboîter. */
.collisionRuleLink{
--link-color:var(--accent);
position:relative; display:inline-flex; align-items:center; gap:4px;
background:linear-gradient(135deg, var(--link-color), color-mix(in srgb, var(--link-color) 70%, #000 30%));
color:#fff; padding:7px 16px 7px 20px; font-size:12.5px; font-weight:600; white-space:nowrap;
clip-path:polygon(0% 0%, 90% 0%, 100% 50%, 90% 100%, 0% 100%, 10% 50%);
margin-left:-12px; z-index:1; animation:collisionLinkIn .3s ease backwards;
clip-path:polygon(0 0, calc(100% - 14px) 0, 100% 50%, calc(100% - 14px) 100%, 0 100%, 14px 50%);
margin-left:-14px; z-index:1; animation:collisionLinkIn .3s ease backwards;
transition:transform .15s ease, filter .15s ease;
}
.collisionRuleLink:first-child{ margin-left:0; clip-path:polygon(0% 0%, 90% 0%, 100% 50%, 90% 100%, 0% 100%); padding-left:14px; border-radius:8px 0 0 8px; }
.collisionRuleLink:first-child{ margin-left:0; clip-path:polygon(0 0, calc(100% - 14px) 0, 100% 50%, calc(100% - 14px) 100%, 0 100%); padding-left:14px; border-radius:8px 0 0 8px; }
.collisionRuleLink:hover{ transform:translateY(-2px) scale(1.04); filter:brightness(1.12); z-index:2; }
.collisionRuleLink[data-kind="trigger"]{ --link-color:#f2994a; }
.collisionRuleLink[data-kind="quete"]{ --link-color:#9b59b6; }
/* "quete" est le seul type de maillon cliquable (voir collision-rules-
editor.js — un onclick posé directement sur le <span>, jamais un
<button> : son style natif grignotait la pointe en clip-path même
après reset), d'où le curseur en plus ici. */
.collisionRuleLink[data-kind="quete"]{ --link-color:#9b59b6; cursor:pointer; }
.collisionRuleLink[data-kind="attaque"]{ --link-color:#e0524d; }
.collisionRuleLink[data-kind="evenement"]{ --link-color:#17a2b8; }
.collisionRuleLink[data-kind="interagir"]{ --link-color:#d4a72c; }
@@ -884,6 +967,13 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
(distincte de la modale de fiche ci-dessus) : couvre tout le viewport
plutôt qu'une boîte centrée, pour laisser toute la place aux 3
colonnes de répliques. */
/* z-index:61 (au-dessus de .floatPanel, à 60, voir plus haut) : sans ça,
les panneaux flottants "Objets"/"Propriétés" restaient peints par-
dessus cette modale PLEIN ÉCRAN. Les boutons "Objets"/"Propriétés"
eux-mêmes (.floatPanelReopenBtn) vivent maintenant dans la barre
d'outils de la scène, plus en position:fixed — ce n'est donc plus qu'un
souci entre CETTE modale et les panneaux flottants encore ouverts. */
.questDialogueModal{ z-index:61; }
.questDialogueModal .modal-content{ width:100vw; height:100vh; max-width:100vw; max-height:100vh; margin:0; }
/* La modale entière est une colonne flex (jamais un scroll global, voir
overflow:hidden ici) : le header/la fiche restent fixes en haut, les 3
@@ -1097,7 +1187,12 @@ code{ background:var(--panel2); border:1px solid var(--border); border-radius:4p
.builderTabBtn:hover{ color:var(--text); }
.builderTabBtn.active{ color:var(--accent); border-bottom-color:var(--accent); font-weight:600; }
.builderTabPanel{ flex:1 1 auto; min-height:0; display:none; flex-direction:column; }
/* overflow-y:auto : contrairement à "screen" (voir .canvasFrame plus haut,
qui défile LUI-MÊME), "collision-rules"/"quests"/"events" n'ont pas de
sous-conteneur dédié au défilement — sans ça, une liste plus haute que
l'espace disponible restait simplement coupée par .builderCanvasArea
(overflow:hidden), au lieu de défiler. */
.builderTabPanel{ flex:1 1 auto; min-height:0; display:none; flex-direction:column; overflow-y:auto; }
.builderTabPanel.active{ display:flex; }
/* Sous-onglets Champs/Données À L'INTÉRIEUR d'un panneau "Modifier un
@@ -1223,6 +1318,17 @@ code{ background:var(--panel2); border:1px solid var(--border); border-radius:4p
position:absolute; top:0; left:0; transform-origin:top left;
transform:scale(calc(100cqw / var(--scene-w)));
}
/* obj.rendered_html (screens/scenes/render_scene_object.py) porte un
z-index PENSÉ POUR L'ÉDITEUR DE SCÈNE (souvent très négatif — un "fond"
à -1000, un personnage juste au-dessus — pour rester sous les cadres/
poignées de sélection de CET éditeur, voir templates/scene_edit.html) :
réutilisé tel quel ici, ce z-index très négatif faisait passer l'objet
SOUS le fond de la carte (.screenPreviewBox, background:#0b0d12),
invisible. L'ordre d'empilement voulu ici (fond, puis personnages/décor
par-dessus) est déjà garanti par l'ORDRE dans lequel screen_preview()
(game_dashboard_simple.html) les liste — z-index:auto (l'ordre du DOM)
suffit donc, sans dépendre de leurs valeurs numériques d'origine. */
.screenPreviewScene > *{ z-index:auto !important; }
.screenPreviewEmpty{
position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
color:var(--forge-text-muted, #8b95a7); font-size:.85em;