Enrichit les declencheurs/actions de scene (clic/survol/affichage, surbrillance/video/son/visibilite/indication/attendre) et fiabilise la pose d'un fond/decor importe
Build and deploy / test-python (push) Successful in 10m59s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

- Ajoute clic/survol/affichage-ecran comme declencheurs, et surbrillance,
  video, son, visibilite, indication, attendre comme actions, utilisables
  aussi bien par l'editeur manuel (menu lateral Objets/Ecran) que par
  Ruby (IA), avec blocs deplacables/supprimables dans une chaine.
- Corrige plusieurs variantes du bug "impossible de poser un objet hors
  du champ de la camera" (troncature du chainage d'actions a 4 maillons,
  fond importe pose a 128x128 au lieu de sa taille reelle, decalage du
  fond au vrai glisser-depose, redimensionnement manuel jamais propage
  au monde).
- Ajoute un vrai glisser-depose depuis la galerie vers la scene, la
  gestion complete de "Mes assets" (sous-sections Fonds/Decors/Sons/
  Videos, suppression, reclassement fond<->decor sans re-upload).
- Ajoute l'upload de son (limite 3 min) et de video (MP4 uniquement,
  limite 5 min), avec validation de la duree reelle du fichier, et une
  replique audio optionnelle dans une bulle de dialogue.
- Fixe la taille de pose d'un objet/decor importe a 200x200 avec une
  boite de collision de 150x150.
- Filtre le selecteur de fichier des actions son/video par type reel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-12 00:28:46 +02:00
co-authored by Claude Sonnet 5
parent b07b231a61
commit 559331f9cf
67 changed files with 4739 additions and 306 deletions
+127 -1
View File
@@ -608,6 +608,45 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
reconnaissable, contrairement à une icône. */
.iconGallery.personnageGallery{ grid-template-columns:repeat(2, 1fr); gap:10px; max-height:420px; }
.personnageThumb{ width:100%; height:100%; object-fit:contain; image-rendering:pixelated; }
/* "Mes assets" — sous-sections Images/Sons/Vidéos (demande explicite :
"voir, utiliser ou supprimer des son, image et vidéo") : une tuile
image garde son bouton de pose (clic/glisser-déposer, voir
iconGalleryTile) avec un ✕ de suppression posé par dessus ; un son/une
vidéo n'ont rien à "poser" — juste une rangée avec leur lecteur natif
(aperçu direct, "voir"/"utiliser" au sens d'écouter/visionner avant de
le choisir dans le sélecteur d'une action) et le même ✕. */
.userAssetTileWrap{ position:relative; }
.userAssetTileWrap .iconGalleryTile{ width:100%; height:100%; }
.userAssetDeleteBtn{
position:absolute; top:-6px; right:-6px; width:18px; height:18px; border-radius:50%;
border:1px solid var(--border); background:#1a1d24; color:var(--muted, #8b95a7);
font-size:10px; line-height:1; display:flex; align-items:center; justify-content:center; cursor:pointer;
padding:0; z-index:2;
}
.userAssetDeleteBtn:hover{ background:#e0524d; color:#fff; border-color:#e0524d; }
.userAssetRow{
display:flex; flex-direction:column; gap:4px; padding:8px; margin-top:8px;
background:var(--panel2); border:1px solid var(--border); border-radius:8px;
}
.userAssetRow:first-child{ margin-top:0; }
.userAssetRow audio, .userAssetRow video{ width:100%; display:block; border-radius:4px; background:#000; }
.userAssetRowFooter{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.userAssetName{ font-size:11.5px; color:var(--text-dim); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; }
.userAssetRowFooter .userAssetDeleteBtn{ position:static; flex-shrink:0; }
/* "→ Fond"/"→ Décor" (voir auth/update_user_asset_scene_kind.py) : bascule
une image DÉJÀ importée entre "Fonds" et "Décors/objets" sans ré-upload
— trouvé en diagnostiquant un "décalage" (une image importée par le
mauvais bouton, ou avant la séparation fond/décor, restait bloquée dans
la mauvaise section). Posé dans .userAssetRowFooter (déjà en
position:static, voir ci-dessus), sous la vignette d'une image. */
.userAssetTileWrap .userAssetRowFooter{ margin-top:4px; }
.userAssetReclassifyBtn{
flex:1; font-size:10.5px; padding:2px 4px; border-radius:4px;
border:1px solid var(--border); background:var(--panel2); color:var(--text-dim);
cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.userAssetReclassifyBtn:hover{ background:var(--panel3, var(--panel2)); color:var(--text); }
.widgetBtnForm{ margin:0; }
.widgetBtn{
width:100%; display:flex; flex-direction:column; align-items:center; gap:4px;
@@ -956,6 +995,41 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
white-space:nowrap; pointer-events:none; z-index:20;
}
/* Bulle de l'action "indication" (voir forgeShowIndicationBubble,
collision-rules-controller.js) — même patron que .collisionInteractBubble
ci-dessus (sœur de l'objet, position calculée en JS), mais un texte
LIBRE (pas juste une touche) qui peut passer à la ligne, et sa propre
classe pour ne jamais interférer avec le cycle d'affichage de la bulle
"Appuie sur X" (minuterie propre, pas liée à l'état "actif" d'un
déclencheur "interagir"). */
.collisionIndicationBubble{
display:none; position:absolute; transform:translate(-50%, -100%); margin-top:-6px;
background:#2a3a5a; color:#fff; border-radius:10px; padding:6px 12px; font-size:13px;
max-width:220px; text-align:center; white-space:normal; pointer-events:none; z-index:20;
box-shadow:0 2px 8px rgba(0,0,0,.3);
}
/* Overlay de l'action "video" (voir forgeShowVideoOverlay,
static/js/play/video-overlay.js) — posé une seule fois sur <body>,
réutilisé pour chaque lecture. "plein_ecran" couvre toute la fenêtre de
jeu ; "incrustation" reste une petite fenêtre en bas à droite, la scène
restant visible autour. */
.forgeVideoOverlay{
display:none; position:fixed; z-index:1000; align-items:center; justify-content:center;
}
.forgeVideoOverlay-pleinEcran{
inset:0; background:rgba(0,0,0,.92);
}
.forgeVideoOverlay-incrustation{
right:24px; bottom:24px; left:auto; top:auto; width:min(40vw, 480px);
}
.forgeVideoOverlay .forgeVideoOverlayPlayer{ max-width:100%; max-height:80vh; display:block; }
.forgeVideoOverlay-incrustation .forgeVideoOverlayPlayer{ width:100%; max-height:none; border-radius:8px; box-shadow:0 4px 20px rgba(0,0,0,.5); }
.forgeVideoOverlay .forgeVideoOverlaySkip{
position:absolute; top:12px; right:12px; background:rgba(0,0,0,.6); color:#fff; border:1px solid rgba(255,255,255,.4);
border-radius:6px; padding:6px 12px; font-size:13px; cursor:pointer;
}
/* ---- Reliquat de l'ancien onglet "🗺️ Quêtes" (retiré, voir onglet
"Déclencheurs" et static/js/triggers/trigger-editor.js) — règles
orphelines gardées telles quelles, aucune ne s'applique plus. ---- */
@@ -1007,6 +1081,30 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
boutons Réplique/Question/Action TOUJOURS tout en bas — ils ajoutent
dans le dialogue du déclencheur ACTIF (voir .questTriggerCol.active),
jamais dans une liste séparée. */
/* Onglet "Déclencheurs" — menu latéral gauche (demande explicite) pour
choisir entre les déclencheurs D'OBJET ("🧩 Objets") et D'ÉCRAN ("🖥️
Écran", voir screens/rendering/screen_triggers.py), plutôt que les
empiler l'un sous l'autre — même patron visuel que .iaCol-left/
.iaConversationItem (chat IA) pour rester cohérent avec le reste de
l'éditeur.*/
.triggersTabLayout{
display:flex; width:100%; flex:1 1 auto; min-height:0;
border:1px solid var(--border); border-radius:10px; overflow:hidden;
}
.triggersTabSidebar{
flex:0 0 190px; border-right:1px solid var(--border); background:var(--panel2);
display:flex; flex-direction:column; gap:4px; padding:10px;
}
.triggersTabSidebarItem{
display:flex; align-items:center; gap:8px; width:100%; padding:10px 12px;
border-radius:8px; border:1px solid transparent; background:transparent;
color:var(--text-dim); text-align:left; cursor:pointer; font-size:13.5px; font-weight:600;
}
.triggersTabSidebarItem:hover{ background:var(--panel); }
.triggersTabSidebarItem.active{ background:var(--panel); border-color:var(--accent); color:var(--text); }
.triggersTabContent{ flex:1 1 auto; min-width:0; min-height:0; display:flex; flex-direction:column; }
.triggersTabPane{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; padding:16px; }
.triggersTabPaneIntro{ margin-bottom:10px; }
.questDialogueColumns{ flex:1; min-height:0; display:flex; }
.questDialogueColumn{
width:90%; margin:0 auto; height:100%; min-height:0; display:flex; flex-direction:column; gap:8px;
@@ -1068,11 +1166,28 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
le losange doit être plus grande"). */
.questTriggerConnector.tall{ height:32px; width:3px; }
.questTriggerConnector.tall::after{ border-width:7px; }
.questTriggerPill{ width:100%; border-radius:100px; padding:8px 14px; font-size:12px; font-weight:700; text-align:center; }
.questTriggerPill{ width:100%; border-radius:100px; padding:8px 14px; font-size:12px; font-weight:700; text-align:center; position:relative; }
.questTriggerPill.trigger{ background:#f2994a; color:#1a1002; }
.questTriggerPill.interagir{ background:#d4a72c; color:#1a1002; }
.questTriggerPill.quete{ background:#9b59b6; color:#fff; }
.questTriggerPill.none{ background:transparent; border:1.5px dashed var(--border); color:var(--muted, #8b95a7); }
/* Contrôles ↑/↓/✕ par bloc étiquette+action (demande explicite : "que
chaque bloc étiquette + action soit déplaçable ou supprimable par
l'utilisateur et par l'IA aussi") — collés au bord droit de la
pastille, discrets tant que la souris n'est pas dessus. */
.questTriggerPillControls{
position:absolute; right:8px; top:50%; transform:translateY(-50%);
display:flex; gap:2px; opacity:.55;
}
.questTriggerPill:hover .questTriggerPillControls{ opacity:1; }
.questTriggerPillCtrlBtn{
width:20px; height:20px; border-radius:6px; border:none; background:rgba(0,0,0,.25);
color:inherit; font-size:11px; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center;
}
.questTriggerPillCtrlBtn:hover{ background:rgba(0,0,0,.45); }
.questTriggerPillCtrlBtn:disabled{ opacity:.3; cursor:default; }
.questTriggerPillCtrlBtn:disabled:hover{ background:rgba(0,0,0,.25); }
.questTriggerPillCtrlBtn-delete:hover{ background:#e0524d; color:#fff; }
/* Rectangle "dialogue" — même largeur que le reste de la chaîne (100% de
son conteneur, comme les pastilles, maquette validée : "le rectangle
@@ -1307,6 +1422,17 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
flex-shrink:0; background:none; border:none; color:rgba(255,255,255,.75); cursor:pointer;
font-size:11px; padding:0; transition:color .15s ease;
}
/* Voix off d'une réplique (demande explicite : "ajouter une réplique
audio") — un sélecteur discret sous le texte, plus un aperçu <audio>
natif SEULEMENT quand une voix off est choisie (voir
triggerBubbleAudioControlHtml, static/js/triggers/trigger-editor.js). */
.questBubbleAudioRow{ margin-top:6px; display:flex; flex-direction:column; gap:4px; }
.questBubbleAudioSelect{
font-size:11px; background:rgba(0,0,0,.2); color:#fff; border:1px solid rgba(255,255,255,.25);
border-radius:4px; padding:2px 4px; max-width:100%;
}
.questBubbleAudioSelect option{ color:#111; }
.questBubbleAudioPreview{ width:100%; height:28px; }
.questBubbleDelete:hover{ color:#fff; }
.listRow.selected{ border-color:var(--accent); background:var(--panel2); }