Ajoute la fonctionnalite quiz autonome/plein ecran a la boite a quiz
Build and deploy / test-python (push) Successful in 12m10s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Introduit la double categorie de modeles (boite de dialogue / page de
quiz plein ecran) avec plein ecran, minuteur, score integre et ecran de
resultat pour les modeles page ; ajoute les modeles "Manga" (boite et
page) et "Classique" (page), pilotables aussi par l'assistant IA Ruby.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-13 15:07:23 +02:00
co-authored by Claude Sonnet 5
parent 559331f9cf
commit 7db4803b93
26 changed files with 3173 additions and 60 deletions
+110 -4
View File
@@ -274,9 +274,20 @@
<div class="builderTabPanel active" data-tab-panel="screen">
<div class="canvasFrame">
<div class="canvas" id="canvas" style="width:{{ world_width }}px; height:{{ world_height }}px; aspect-ratio:auto; margin:0 auto; position:relative; overflow:hidden; background:#1a1d24;"
<!-- Un quiz plein écran fait disparaître la notion de caméra
(demande explicite) — pas seulement le cadre "🎥 Champ de la
caméra" (déjà masqué plus bas), mais le CANEVAS lui-même :
sans ce cas particulier, il restait dimensionné en pixels
fixes (world_width/world_height, une notion de caméra/scène
nominale) — .canvasElement.isQuizFullscreenPreview a beau
remplir 100% de CE canevas, le canevas restait lui-même de la
taille de la caméra, laissant une bande vide visible autour.
width/height:100% (voir la règle .canvas déjà prévue pour ça
dans static/style.css) fait remplir tout .canvasFrame à la
place, indépendamment de toute taille de scène/caméra. -->
<div class="canvas" id="canvas" style="{{ 'width:100%; height:100%;' if any_quiz_box_fullscreen else 'width:' + world_width|string + 'px; height:' + world_height|string + 'px;' }} aspect-ratio:auto; margin:0 auto; position:relative; overflow:hidden; background:#1a1d24;"
ondragover="onCanvasDragOver(event)" ondrop="onCanvasDrop(event)">
{% if world_width > screen.scene_width or world_height > screen.scene_height %}
{% if not any_quiz_box_fullscreen and (world_width > screen.scene_width or world_height > screen.scene_height) %}
<!-- "🎥 Champ de la caméra" : le monde (ce canevas, voir
world_width/world_height ci-dessus, routes/scenes/
scene_edit_view.py) peut être plus grand que la scène
@@ -286,11 +297,14 @@
controller.js::forgeUpdateSceneCamera), pour que le monde
agrandi reste lisible plutôt qu'une simple étendue grise
sans repère. Purement visuel (pointer-events:none) : ne
gêne jamais la sélection/le glisser d'un objet dessous. -->
gêne jamais la sélection/le glisser d'un objet dessous.
Masqué dès qu'un quiz PLEIN ÉCRAN existe sur cet écran
(demande explicite : "la notion de caméra disparaît") —
il n'a plus aucun sens une fois que ce widget couvre tout. -->
<div class="sceneCameraFrame" style="width:{{ screen.scene_width }}px; height:{{ screen.scene_height }}px;" title="Champ de la caméra en jeu ({{ screen.scene_width }}×{{ screen.scene_height }} px)"></div>
{% endif %}
{% for o in objects %}
<div class="canvasElement {{ 'selected' if o.id == selected_id else '' }}" id="el-{{ o.id }}"
<div class="canvasElement {{ 'selected' if o.id == selected_id else '' }} {{ 'isQuizFullscreenPreview' if o.quiz_fullscreen else '' }}" id="el-{{ o.id }}"
style="position:absolute; left:{{ o.x }}px; top:{{ o.y }}px; width:{{ o.width }}px; height:{{ o.height }}px;"
onmousedown="onSceneObjectMouseDown(event, {{ o.id }})">
<div class="canvasElementInner">{{ o.rendered_html|safe }}</div>
@@ -562,6 +576,98 @@
</details>
{% endif %}
{% if obj.kind == 'quiz_box' %}
<!-- "⚙️ Options du quiz" (demande explicite : "étendre les
fonctionnalités de la boîte à quiz pour les RH qui veulent
juste faire un quiz") — voir screens/rendering/quiz_box_config.py,
routes/scenes/scene_object_quiz_config.py. Plein écran/minuteur,
DISTINCT de "🎨 Style" ci-dessus (couleurs/police, partagé avec
dialogue_box/score_widget). -->
<details class="propGroup" open>
<summary>Options du quiz</summary>
<div class="propGroupBody">
<label class="checkbox is-small">
<input type="checkbox" id="quiz-fullscreen" data-was-checked="{{ '1' if quiz_box_config.fullscreen else '0' }}" {{ 'checked' if quiz_box_config.fullscreen else '' }} onchange="saveQuizBoxConfig({{ obj.id }})">
Plein écran
</label>
<div class="field mt-2">
<label class="label is-small">Minuteur</label>
<div class="control">
<select class="input is-small" id="quiz-timer-mode" onchange="saveQuizBoxConfig({{ obj.id }})">
{% for mode in quiz_box_timer_modes %}
<option value="{{ mode }}" {{ 'selected' if quiz_box_config.timer_mode == mode else '' }}>
{{ {'aucun': 'Aucun', 'question': 'Par question', 'quiz': 'Pour tout le quiz'}[mode] }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="field">
<label class="label is-small">Durée (secondes)</label>
<div class="control">
<input class="input is-small" type="number" id="quiz-timer-seconds" min="3" max="600" value="{{ quiz_box_config.timer_seconds }}" onchange="saveQuizBoxConfig({{ obj.id }})">
</div>
</div>
<p class="help">Le plein écran et le minuteur ne s'appliquent qu'au jeu (jamais dans cet éditeur).</p>
</div>
</details>
<!-- "🖼️ Modèles" — DEUX catégories bien distinctes (demande
explicite), une SEULE des deux visible/modifiable à la fois
selon "Plein écran" ci-dessus (les deux valeurs restent
conservées indépendamment, voir screens/rendering/
quiz_box_config.py) :
- "boîte de dialogue" (hors plein écran) : variantes de forme/
couleur sur une structure proche de "Classique" ;
- "page de quiz" (plein écran) : thème complet et autonome,
structure HTML propre à CHAQUE modèle (voir
screens/rendering/quiz_box_templates.py,
render_quiz_box_page_manga pour le premier).
Ce <div> caché retient la valeur de CELUI des deux réglages
qui n'a pas de galerie affichée ici, pour que saveQuizBoxConfig
(scene-editor.js) ne l'écrase jamais par erreur en l'absence de
sa grille. -->
<div id="quizBoxTemplateMeta" data-dialog-template="{{ quiz_box_config.dialog_template }}" data-page-template="{{ quiz_box_config.page_template }}" style="display:none;"></div>
{% if quiz_box_config.fullscreen %}
<details class="propGroup" open>
<summary>Modèles — page de quiz</summary>
<div class="propGroupBody">
<p class="help mb-2">Visibles seulement en plein écran — un thème complet et autonome par modèle (police, formes, couleurs et structure propres à chacun).</p>
<div class="quizTemplateGrid" id="quizPageTemplateGrid" data-object-id="{{ obj.id }}">
{% for key in quiz_box_page_templates %}
<button type="button" class="quizTemplateTile {{ 'is-selected' if quiz_box_config.page_template == key else '' }}"
data-template="{{ key }}" onclick="selectQuizBoxPageTemplate({{ obj.id }}, '{{ key }}')">
<span class="quizTemplatePreviewImg">
<img src="{{ url_for('static', filename='quiz-templates/' + key + '.png') }}" alt="Aperçu du modèle « {{ quiz_box_page_template_labels[key] }} »" loading="lazy">
</span>
<span class="quizTemplateName">{{ quiz_box_page_template_labels[key] }}</span>
</button>
{% endfor %}
</div>
</div>
</details>
{% else %}
<details class="propGroup" open>
<summary>Modèles — boîte de dialogue</summary>
<div class="propGroupBody">
<p class="help mb-2">Visibles seulement hors plein écran — un thème complet et autonome par modèle (police, formes, couleurs et structure propres à chacun).</p>
<div class="quizTemplateGrid" id="quizDialogTemplateGrid" data-object-id="{{ obj.id }}">
{% for key in quiz_box_dialog_templates %}
<button type="button" class="quizTemplateTile {{ 'is-selected' if quiz_box_config.dialog_template == key else '' }}"
data-template="{{ key }}" onclick="selectQuizBoxDialogTemplate({{ obj.id }}, '{{ key }}')">
<span class="quizTemplatePreviewImg">
<img src="{{ url_for('static', filename='quiz-templates/' + key + '.png') }}" alt="Aperçu du modèle « {{ quiz_box_dialog_template_labels[key] }} »" loading="lazy">
</span>
<span class="quizTemplateName">{{ quiz_box_dialog_template_labels[key] }}</span>
</button>
{% endfor %}
</div>
</div>
</details>
{% endif %}
{% endif %}
{% if obj.kind not in ('fond', 'dialogue_box', 'quiz_box', 'score_widget') %}
<!-- Boîte de collision (voir screens/rendering/collision_settings.py,
static/js/play/conditions.js::elementsOverlap) : posée AUTOMATIQUEMENT