From 077af2234db3eda58658b9c3f6346f2b3ee138c2 Mon Sep 17 00:00:00 2001 From: william Date: Thu, 3 Sep 2026 12:08:41 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20"+=20R=C3=A9plique"=20qui=20rempla=C3=A7a?= =?UTF-8?q?it=20la=20bulle=20au=20lieu=20d'en=20ajouter=20une?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- static/js/quests/quest-editor.js | 13 ++++++++++--- static/style.css | 24 ++++++++++++++++-------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/static/js/quests/quest-editor.js b/static/js/quests/quest-editor.js index 8c28c345..a32b685e 100644 --- a/static/js/quests/quest-editor.js +++ b/static/js/quests/quest-editor.js @@ -321,12 +321,19 @@ function questUpdateField(questId, field, value) { // Dialogues : toujours les 3 colonnes ENTIÈRES postées ensemble (voir // db/quests/set_quest_dialogues.py) — jamais une réplique isolée. +// N'ADOPTE JAMAIS la réponse du serveur comme nouvel état local (bug +// corrigé : sanitize_quest_dialogues.py rejette toute réplique au texte +// encore vide — juste ajoutée, pas encore tapée — donc chaque +// enregistrement renvoyait un tableau amputé de la bulle en cours +// d'écriture ; l'adopter effaçait silencieusement la bulle que l'auteur +// venait d'ajouter, et le clic suivant sur "+ Réplique" semblait la +// "remplacer" au lieu d'en ajouter une seconde). L'état local, construit +// par des mutations client explicites, est déjà la seule source de +// vérité pendant l'édition. function questSaveDialogues(quest) { fetch('/game/' + GAME_SLUG + '/quests/' + quest.id + '/dialogues', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ dialogues: quest.dialogues }), - }) - .then(function (r) { return r.json(); }) - .then(function (json) { if (json.dialogues) quest.dialogues = json.dialogues; }); + }); } // Alterne "Joueur" <-> le premier personnage nommé (ou "PNJ" faute de diff --git a/static/style.css b/static/style.css index b96649fa..e9a92e02 100644 --- a/static/style.css +++ b/static/style.css @@ -805,23 +805,31 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d plutôt qu'une boîte centrée, pour laisser toute la place aux 3 colonnes de répliques. */ .questDialogueModal .modal-content{ width:100vw; height:100vh; max-width:100vw; max-height:100vh; margin:0; } -.questDialogueModal .box{ width:100%; height:100%; border-radius:0; overflow-y:auto; } +/* 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 + colonnes de répliques se partagent TOUT le reste de la hauteur + (demandé explicitement) et défilent CHACUNE indépendamment (voir + .questDialogueColumnBubbles plus bas), jamais toute la page. */ +.questDialogueModal .box{ width:100%; height:100%; border-radius:0; overflow:hidden; display:flex; flex-direction:column; } .questDialogueModalContent{ width:100%; height:100%; max-width:100%; } -.questDialogueModalHeader{ margin-bottom:10px; } +#questDialogueModalBody{ display:flex; flex-direction:column; flex:1; min-height:0; } +.questDialogueModalHeader{ flex:0 0 auto; margin-bottom:10px; } .questFieldsGrid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(160px, 1fr)); gap:10px; margin-bottom:14px; } .questField{ display:flex; flex-direction:column; gap:4px; font-size:12px; font-weight:600; } .questField-wide{ grid-column:span 2; } -.questDialogueColumns{ display:grid; grid-template-columns:repeat(3, minmax(220px, 1fr)); gap:14px; overflow-x:auto; } +.questDialogueColumns{ flex:1; min-height:0; display:grid; grid-template-columns:repeat(3, minmax(220px, 1fr)); gap:14px; } .questDialogueColumn{ background:rgba(0,0,0,.15); border:1px solid var(--border); border-radius:12px; padding:10px; - display:flex; flex-direction:column; gap:8px; min-height:160px; + display:flex; flex-direction:column; gap:8px; height:100%; min-height:0; } -.questDialogueColumnTitle{ margin:0 0 2px; font-size:13px; text-align:center; } +.questDialogueColumnTitle{ flex:0 0 auto; margin:0 0 2px; font-size:13px; text-align:center; } /* Bulles alignées VERTICALEMENT, centrées dans la colonne, reliées par un trait entre chacune (voir ::before sur .questBubbleRow ci-dessous) — - demandé explicitement plutôt qu'un alignement gauche/droite. */ -.questDialogueColumnBubbles{ display:flex; flex-direction:column; align-items:center; flex:1; padding-top:4px; } -.questDialogueAddBtn{ align-self:center; padding:6px 14px; font-size:12px; margin-top:4px; } + demandé explicitement plutôt qu'un alignement gauche/droite. Seule + CETTE zone défile (flex:1 + overflow-y:auto) — le titre de colonne et + le bouton "+ Réplique" restent fixes, en haut/en bas. */ +.questDialogueColumnBubbles{ display:flex; flex-direction:column; align-items:center; flex:1; min-height:0; overflow-y:auto; padding-top:4px; } +.questDialogueAddBtn{ flex:0 0 auto; align-self:center; padding:6px 14px; font-size:12px; margin-top:4px; } .questBubbleRow{ display:flex; justify-content:center; width:100%; position:relative; } .questBubbleRow:not(:first-child)::before{