Ajoute l'assistant IA "Ruby" (Claude + Scenario) et corrige plusieurs bugs de la scène
Intègre un chat IA capable de manipuler la scène via les mêmes fonctions que l'éditeur manuel (objets, variables, déclencheurs, images générées), avec conversations multiples par écran façon Claude. Corrige au passage le rafraîchissement pjax hors-ordre, l'onglet IA/déclencheurs vide après sélection d'un objet, la comparaison de booléens dans les conditions, et le blocage du glisser-déposer hors du cadre caméra après un redimensionnement de fond par l'IA. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
ab6635eee0
commit
b07b231a61
+43
-33
@@ -65,11 +65,37 @@
|
||||
sélectionnable en cliquant directement dessus sur le canevas
|
||||
(voir onSceneObjectMouseDown, static/js/scenes/scene-editor.js). -->
|
||||
|
||||
<!-- "Mes assets" (voir auth/user_assets_dir.py) : bibliothèque PAR
|
||||
COMPTE (pas par jeu) — upload manuel ou généré par l'IA (onglet
|
||||
"IA", Phase 2), réutilisable d'un projet à l'autre du même
|
||||
compte. Posée comme un "decor"/"fond" ordinaire (voir
|
||||
screens/scenes/add_scene_object.py::image_url). PREMIER menu du
|
||||
panneau (demande explicite) : le point d'entrée le plus direct
|
||||
pour un créateur qui vient d'importer/générer une image. -->
|
||||
<details class="propGroup" open>
|
||||
<summary>Mes assets</summary>
|
||||
<div class="propGroupBody">
|
||||
<div class="iconGallery personnageGallery">
|
||||
{% for asset in user_assets %}
|
||||
<button type="button" class="iconGalleryTile" title="{{ asset.original_name or 'Asset' }}"
|
||||
onclick="addSceneObject('decor', null, null, {{ asset.id }})">
|
||||
<img src="{{ url_for('serve_user_asset', user_id=asset.user_id, filename=asset.filename) }}"
|
||||
alt="{{ asset.original_name or 'Asset' }}" class="personnageThumb">
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<label class="button is-small is-fullwidth mt-2">
|
||||
+ Importer une image
|
||||
<input type="file" accept="image/*" style="display:none" onchange="uploadUserAsset(this)">
|
||||
</label>
|
||||
<p class="help">Réutilisable dans tous tes projets — jamais visible par les autres comptes.</p>
|
||||
</div>
|
||||
</details>
|
||||
<!-- Galerie de personnages Forge (VRAIES miniatures) — cliquer une
|
||||
tuile pose un personnage déjà configuré, animé immédiatement (voir
|
||||
personnage-preview.js, réutilisé tel quel : ne dépend que de
|
||||
data-personnage, pas de la table d'origine). -->
|
||||
<details class="propGroup" open>
|
||||
<details class="propGroup">
|
||||
<summary>Personnages</summary>
|
||||
<div class="propGroupBody">
|
||||
<div class="iconGallery personnageGallery">
|
||||
@@ -126,6 +152,7 @@
|
||||
<button type="button" class="builderTabBtn active" data-tab="screen" onclick="switchBuilderTab('screen')">Scène</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="triggers" onclick="switchBuilderTab('triggers')">Déclencheurs</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="variables" onclick="switchBuilderTab('variables')">Variables</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="ia" onclick="switchBuilderTab('ia')">Générer avec l'IA</button>
|
||||
{% if game.onboarding_type != 'rpg' %}
|
||||
<!-- Événements personnalisés (voir db.list_custom_events) : aucun
|
||||
moyen de les DÉCLENCHER depuis un jeu RPG pour l'instant — le
|
||||
@@ -203,8 +230,7 @@
|
||||
<button type="button" class="button is-small primary" onclick="toggleDashCreate('variables')">+ Créer une variable</button>
|
||||
</div>
|
||||
<p class="hint">Accessibles depuis n'importe quelle scène (conditions, actions "Modifier une variable"). Le nom ne peut plus changer une fois créé.</p>
|
||||
<form method="post" action="{{ url_for('create_global_var', slug=game.slug) }}" class="dashCreateBar" id="dashCreate-variables" style="display:none;">
|
||||
<input type="hidden" name="screen_id" value="{{ screen.id }}">
|
||||
<form class="dashCreateBar" id="dashCreate-variables" style="display:none;" onsubmit="return createGlobalVariable(event);">
|
||||
<input type="text" name="name" placeholder="NOM (ex. score_total)" required autocomplete="off">
|
||||
<select name="type">
|
||||
{% for key, t in global_variable_types.items() %}
|
||||
@@ -218,44 +244,26 @@
|
||||
<button type="submit" class="primary">Créer</button>
|
||||
</form>
|
||||
|
||||
{% if global_variables %}
|
||||
{% for v in global_variables %}
|
||||
<form method="post" action="{{ url_for('global_var_edit', slug=game.slug, var_id=v.id) }}" id="varEditForm{{ v.id }}">
|
||||
<input type="hidden" name="screen_id" value="{{ screen.id }}">
|
||||
</form>
|
||||
{% endfor %}
|
||||
<div class="fieldsTableWrap">
|
||||
<table class="table is-fullwidth is-hoverable fieldsTable">
|
||||
<thead><tr><th>Nom</th><th>Type</th><th>Valeur</th><th>Par joueur</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tbody id="globalVariablesBody">
|
||||
{% for v in global_variables %}
|
||||
<tr>
|
||||
<td><code>{{ v.name }}</code></td>
|
||||
<td>
|
||||
<select form="varEditForm{{ v.id }}" name="type" aria-label="Type de « {{ v.name }} »">
|
||||
{% for key, t in global_variable_types.items() %}
|
||||
<option value="{{ key }}" {{ 'selected' if v.type == key else '' }}>{{ t.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" form="varEditForm{{ v.id }}" name="value" value="{{ v.value or '' }}" placeholder="Valeur" aria-label="Valeur de « {{ v.name }} »"></td>
|
||||
<td><span class="hint">{{ 'Oui' if v.per_player else 'Non (partagée)' }}</span></td>
|
||||
<td class="fieldsTableActions">
|
||||
<button type="submit" form="varEditForm{{ v.id }}" class="button is-small" title="Enregistrer">💾</button>
|
||||
<form method="post" action="{{ url_for('global_var_delete', slug=game.slug, var_id=v.id) }}"
|
||||
onsubmit="return confirm('Supprimer la variable « {{ v.name }} » ? Toute condition/action qui la référence encore cessera de fonctionner.');">
|
||||
<input type="hidden" name="screen_id" value="{{ screen.id }}">
|
||||
<button type="submit" class="button is-small is-danger" title="Supprimer">🗑️</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% include "_global_variable_row.html" %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="hint">Aucune variable pour l'instant — crée la première ci-dessus.</p>
|
||||
{% endif %}
|
||||
<p class="hint" id="globalVariablesEmptyHint" {{ 'style=display:none;' if global_variables else '' }}>Aucune variable pour l'instant — crée la première ci-dessus.</p>
|
||||
</div>
|
||||
|
||||
<!-- Onglet "🤖 IA" (voir plan Phase 1) : scopé à CET écran uniquement
|
||||
(pas tout le jeu) — le chat pilote un agent qui n'utilise que les
|
||||
mêmes fonctions que l'éditeur manuel (voir ai/tools.py, Phase 2),
|
||||
pour que tout ce qu'il pose reste relisable/modifiable ici. Rendu
|
||||
par static/js/ia/ia-chat.js dans #iaTabPanelBody. -->
|
||||
<div class="builderTabPanel" id="iaTabPanel" data-tab-panel="ia">
|
||||
<div id="iaTabPanelBody" data-screen-id="{{ screen.id }}"></div>
|
||||
</div>
|
||||
|
||||
{% if game.onboarding_type != 'rpg' %}
|
||||
@@ -646,6 +654,8 @@
|
||||
<script src="{{ url_for('static', filename='js/screen_edit/builder-tabs.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/screen_edit/personnage-preview.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/scenes/scene-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/scenes/global-variables.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/triggers/trigger-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/scenes/collision-rules-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/ia/ia-chat.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user