Met en place les fondations du template Bulma décrit dans regles/FORGE_ENGINE_TEMPLATE_BULMA.md : palette orange/ambre Forge (--accent:#ff5f2e/--accent-2:#ffb020), typographie, rayons — appliqués en recompilant Bulma lui-même plutôt qu'en le surchargeant après coup en CSS, pour recolorer automatiquement ses composants internes (tags, notifications, dropdowns...) sans avoir à les surcharger un par un. - Bulma 0.9.4 vendoré en local (styles/bulma/, source Sass classique $variable + @import) — PAS 1.0.2 (la version jusqu'ici en CDN) : 1.0.2 utilise le nouveau système de modules @use/@forward, que libsass (choisi pour rester 100% Python, sans Node/npm) ne sait pas compiler (testé : il ignore silencieusement le @use au lieu de le traiter). 0.9.4 est la dernière version compatible avec libsass et couvre à l'identique tous les composants utilisés ici (boutons, tableaux, onglets, modales, formulaires, navbar). - requirements.txt : +libsass (pip pur, aucun binaire/Node.js). - styles/forge-theme.scss (nouveau, point d'entrée) : variables Sass Bulma ($primary, $radius...) posées avant l'import, tokens Forge exposés en :root (--forge-bg, --accent, --gradient, --status-*...) avec des alias vers les noms de variables déjà utilisés par tout le CSS custom existant (--bg/--panel/--border/--text/--danger...) — pas besoin de renommer les ~600 lignes de règles déjà écrites, seules leurs VALEURS changent. - styles/forge-custom.scss : ancien static/style.css, structurellement inchangé — seuls les hex/rgba en dur qui échappaient aux variables (ancien accent bleu #5b8cff, danger #e2685f, couleurs de types de nœuds du graphe de logique, fond du QR code recovery...) sont remplacés par les tokens de la charte. - build_css.py (nouveau) : compile styles/forge-theme.scss en static/style.css via libsass — un seul fichier, un seul <link> inchangé dans les templates, à relancer manuellement après toute modification sous styles/. - base.html/play.html : suppression du <link> CDN Bulma (auto-hébergé désormais), ajout d'un favicon (absent jusqu'ici) et du vrai logo Forge dans la navbar (assets/*.svg copiés dans static/branding/, seul dossier réellement servi par Flask). 197 tests toujours verts (aucune assertion sur des valeurs CSS). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
5.0 KiB
HTML
87 lines
5.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Forge Engine{% endblock %}</title>
|
|
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='branding/forge-favicon.svg') }}">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
|
<script>window.CSRF_TOKEN = {{ csrf_token()|tojson }};</script>
|
|
<script src="{{ url_for('static', filename='csrf_fetch.js') }}"></script>
|
|
{% block extra_head %}{% endblock %}
|
|
</head>
|
|
<body class="{% block body_class %}{% endblock %}">
|
|
<!-- #pageChrome : tout ce qui est en dehors de <main> mais doit quand même
|
|
changer d'une page à l'autre (barre du jeu comprise) — un seul bloc
|
|
stable pour que pjax.js puisse le remplacer d'un coup à chaque
|
|
navigation (voir swapDocument() dans pjax.js). Avant l'ajout de ce
|
|
conteneur, pjax.js cherchait un <header class="topbar"> qui n'a jamais
|
|
existé (la barre est un <nav>, pas un <header>) : la barre de
|
|
navigation du jeu (ajoutée après pjax.js) ne changeait donc jamais
|
|
pendant la navigation pjax — absente en arrivant sur un jeu tant qu'on
|
|
ne rechargeait pas la page en entier (Ctrl+F5), et inversement
|
|
laissée en place en revenant sur l'accueil (liste des jeux) où elle
|
|
n'a pourtant rien à faire. -->
|
|
<div id="pageChrome">
|
|
<nav class="navbar topbar" role="navigation" aria-label="main navigation">
|
|
<div class="navbar-brand">
|
|
<a href="{{ url_for('index') }}" class="navbar-item brand">
|
|
<img src="{{ url_for('static', filename='branding/forge-logo.svg') }}" alt="" width="32" height="32" style="border-radius:8px;">
|
|
<strong class="forge-gradient-title">Forge Engine</strong>
|
|
</a>
|
|
</div>
|
|
{% if g.get('current_user') %}
|
|
<div class="navbar-end" style="margin-left:auto; display:flex; align-items:center; gap:10px; padding-right:12px;">
|
|
<a href="{{ url_for('profile') }}" class="hint" style="text-decoration:underline;">{{ g.current_user.prenom }} {{ g.current_user.nom }}{{ ' (admin)' if g.current_user.role == 'admin' else '' }}</a>
|
|
<form method="post" action="{{ url_for('logout') }}"><button type="submit" class="button is-small">Déconnexion</button></form>
|
|
</div>
|
|
{% endif %}
|
|
</nav>
|
|
{% if game is defined and game %}
|
|
<!-- Barre toujours visible entre les différentes pages d'UN MÊME jeu.
|
|
Écrans/Éléments de jeu/Variables/Objets n'ont plus leur propre page —
|
|
tout ça vit désormais dans les onglets du tableau de bord (voir
|
|
game_dashboard.html) — donc un seul lien y mène. Remplace le fil
|
|
d'Ariane (retiré, devenu redondant) : n'apparaît que si la route a mis
|
|
un `game` dans le contexte du template (déjà fait par toutes les
|
|
pages d'un jeu) — absente sur l'accueil (liste des jeux). "Jouer" est
|
|
délibérément le DERNIER lien — une action à part (ouvre l'aperçu
|
|
jouable dans un nouvel onglet), pas une page de gestion de plus. -->
|
|
<nav class="gameNavBar" aria-label="Navigation du jeu">
|
|
<a href="{{ url_for('game_dashboard', slug=game.slug) }}" class="gameNavLink {{ 'active' if request.path == '/game/' ~ game.slug else '' }}" title="Écrants, objets, templates, variables">📊 Tableau de bord</a>
|
|
<a href="{{ url_for('game_play', slug=game.slug) }}" target="_blank" class="gameNavLink" title="Aperçu jouable du jeu">▶️ Jouer</a>
|
|
</nav>
|
|
{% endif %}
|
|
</div>
|
|
<main class="content {% block content_class %}{% endblock %}">
|
|
{% set recovery_codes = pop_recovery_codes() %}
|
|
{% if recovery_codes %}
|
|
<!-- À L'INTÉRIEUR de <main> (et pas à côté de #pageChrome) : pjax.js ne
|
|
remplace jamais que #pageChrome et main.content d'une navigation à
|
|
l'autre (voir swapDocument() dans pjax.js) — posé ailleurs, ce modal
|
|
serait bien présent dans le HTML renvoyé par le serveur juste après
|
|
l'inscription, mais jamais copié dans le DOM réellement affiché
|
|
puisque la redirection qui suit la confirmation de la 2FA passe par
|
|
une navigation pjax (fetch), pas un vrai rechargement de page. -->
|
|
<div class="modal is-active" id="recoveryCodesModal">
|
|
<div class="modal-background"></div>
|
|
<div class="modal-content">
|
|
<div class="box">
|
|
<h2 class="title is-4">Tes codes de récupération</h2>
|
|
<p class="hint mb-4">Note ces 10 codes en sécurité (gestionnaire de mots de passe, papier...) : chacun ne fonctionne qu'une seule fois et permet de te connecter si tu perds l'accès à ton application d'authentification. Ils ne seront plus jamais affichés.</p>
|
|
<div class="recoveryCodesGrid">
|
|
{% for code in recovery_codes %}
|
|
<code class="recoveryCode">{{ code }}</code>
|
|
{% endfor %}
|
|
</div>
|
|
<button type="button" class="button primary is-fullwidth mt-4" onclick="document.getElementById('recoveryCodesModal').remove()">J'ai noté mes codes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
<script src="{{ url_for('static', filename='pjax.js') }}"></script>
|
|
</body>
|
|
</html>
|