É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>
This commit is contained in:
william
2026-09-02 17:23:42 +02:00
co-authored by Claude Sonnet 5
parent ee270d0286
commit b64541856f
2 changed files with 112 additions and 32 deletions
+64 -12
View File
@@ -676,28 +676,80 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
métaphore "pièces qui s'emboîtent" demandée, sans un vrai graphe à
tracer à la souris (l'assistant "+ Action" construit la chaîne pas à
pas, voir openCollisionRuleModal). */
.collisionRulesCards{ display:flex; flex-direction:column; gap:14px; margin-top:12px; }
@keyframes collisionCardIn{ from{ opacity:0; transform:translateY(10px) scale(.97); } to{ opacity:1; transform:none; } }
@keyframes collisionLinkIn{ from{ opacity:0; transform:translateX(-10px) scale(.9); } to{ opacity:1; transform:none; } }
@keyframes collisionChoiceIn{ from{ opacity:0; transform:translateY(8px) scale(.92); } to{ opacity:1; transform:none; } }
.collisionRulesCards{ display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); gap:18px; margin-top:14px; }
.collisionRuleCard{
background:var(--panel); border:1px solid var(--border); border-radius:10px; padding:14px;
position:relative; background:linear-gradient(165deg, var(--panel), var(--panel2));
border:1px solid var(--border); border-radius:16px; padding:16px;
box-shadow:0 6px 18px rgba(0,0,0,.18); animation:collisionCardIn .35s ease backwards;
transition:transform .18s ease, box-shadow .18s ease;
}
.collisionRuleCardHeader{ display:flex; align-items:center; gap:10px; margin-bottom:8px; }
.collisionRuleCard:hover{ transform:translateY(-3px); box-shadow:0 10px 26px rgba(0,0,0,.28); }
.collisionRuleCardHeader{ display:flex; align-items:center; gap:10px; margin-bottom:12px; }
.collisionRuleCardThumb{
width:36px; height:36px; border-radius:6px; overflow:hidden; background:var(--panel2);
width:40px; height:40px; border-radius:10px; overflow:hidden; background:var(--panel2);
display:flex; align-items:center; justify-content:center; flex-shrink:0;
border:1px solid var(--border); box-shadow:inset 0 0 0 2px rgba(255,255,255,.03);
}
.collisionRuleCardThumb img{ max-width:100%; max-height:100%; object-fit:contain; }
.collisionRuleCardTitle{ margin:0; font-size:14px; }
.collisionRuleCardRules{ display:flex; flex-direction:column; gap:8px; margin-bottom:10px; }
.collisionRuleCardTitle{ margin:0; font-size:15px; font-weight:700; }
.collisionRuleCardRules{ display:flex; flex-direction:column; gap:10px; margin-bottom:14px; }
/* Une règle = une "chaîne" de maillons en forme de pièces qui s'emboîtent
(déclencheur -> action -> sous-action éventuelle), pas une simple liste
de pastilles à plat — chaque maillon chevauche le précédent via une
marge négative + une pointe en clip-path, coloré selon son type. */
.collisionRuleChain{
display:flex; align-items:center; justify-content:space-between; gap:8px;
background:var(--panel2); border:1px solid var(--border); border-radius:8px; padding:6px 8px;
display:flex; align-items:center; gap:10px; background:rgba(0,0,0,.15);
border:1px solid var(--border); border-radius:12px; padding:8px 10px;
}
.collisionRuleChainLinks{ display:flex; align-items:center; flex-wrap:wrap; gap:6px; }
.collisionRuleChainLinks{ display:flex; align-items:center; flex-wrap:wrap; row-gap:6px; }
.collisionRuleLink{
background:var(--accent); color:#fff; border-radius:12px; padding:3px 10px; font-size:12px; white-space:nowrap;
--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;
transition:transform .15s ease, filter .15s ease;
}
.collisionRuleArrow{ color:var(--forge-text-muted, #8b95a7); font-size:12px; }
.collisionWizardChoices{ display:flex; flex-direction:column; }
.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: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; }
.collisionRuleLink[data-kind="attaque"]{ --link-color:#e0524d; }
.collisionRuleLink[data-kind="evenement"]{ --link-color:#17a2b8; }
.collisionRuleLink[data-kind="interagir"]{ --link-color:#d4a72c; }
.collisionRuleChainDelete{
margin-left:auto; flex-shrink:0; width:26px; height:26px; border-radius:50%; border:none;
background:rgba(224,82,77,.15); color:#e0524d; display:flex; align-items:center; justify-content:center;
cursor:pointer; transition:background .15s ease, transform .15s ease; font-size:13px;
}
.collisionRuleChainDelete:hover{ background:#e0524d; color:#fff; transform:scale(1.08); }
.collisionRuleAddBtn{
display:inline-flex; align-items:center; gap:6px; background:linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #000 30%));
color:#fff; border:none; border-radius:999px; padding:8px 16px; font-size:13px; font-weight:700;
cursor:pointer; box-shadow:0 4px 12px rgba(0,0,0,.25); transition:transform .15s ease, box-shadow .15s ease;
}
.collisionRuleAddBtn:hover{ transform:translateY(-2px) scale(1.03); box-shadow:0 6px 18px rgba(0,0,0,.35); }
.collisionRuleAddBtn:active{ transform:translateY(0) scale(.98); }
/* Assistant "+ Action" — une grille de cartes-choix animées en cascade
plutôt qu'une pile de boutons pleine largeur. */
.collisionWizardChoices{ display:grid; grid-template-columns:repeat(auto-fill, minmax(190px, 1fr)); gap:10px; }
.collisionWizardChoice{
display:flex; flex-direction:column; align-items:flex-start; gap:4px; text-align:left;
background:var(--panel2); border:1px solid var(--border); border-radius:12px; padding:12px 14px;
cursor:pointer; font-size:13.5px; font-weight:600; color:inherit;
animation:collisionChoiceIn .25s ease backwards; transition:transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.collisionWizardChoice:hover{ transform:translateY(-3px) scale(1.02); border-color:var(--accent); box-shadow:0 8px 18px rgba(0,0,0,.25); }
.collisionWizardChoice .emoji{ font-size:22px; }
.collisionInteractBubble{ display:none; position:absolute; left:50%; bottom:100%; transform:translateX(-50%); margin-bottom:6px; background:#111; color:#fff; border-radius:10px; padding:4px 10px; font-size:12px; white-space:nowrap; pointer-events:none; z-index:20; }
.listRow.selected{ border-color:var(--accent); background:var(--panel2); }