Nouvel attribut max_width (vide par défaut = pleine largeur, inchangé) sur les kinds titre/paragraphe, réglable depuis leur panneau Propriétés. Le sous-titre de la page de garde du thème Sécurité Incendie l'utilise (60ch) pour rester conforme à la maquette d'origine, qui ne l'étirait pas sur toute la largeur de la page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
15 KiB
document_engine/labels/
Catalogue statique des types d'éléments du support de formation : bibliothèque groupée par catégorie (panneau gauche de l'éditeur), libellés d'affichage, et attributs par défaut posés à la création de chaque type.
CONTENT_KINDS: tuple[str, ...]
("titre", "paragraphe", "image", "bouton", "liste_puces", "liste_numerotee", "badge", "carte") — éléments du flux, peuvent être
top-niveau ou enfants d'une rangée. "liste_puces"/"liste_numerotee"
partagent la même structure d'attributs (items) ; c'est le kind
lui-même qui décide <ul> ou <ol> au rendu (voir
document_engine/rendering/render_document_element.py::_render_list),
jamais un attribut ordered redondant. "badge" et "carte" sont du
contenu PUR, sans aucun attribut de style/couleur/forme (décision du
24/09/2026 : le moteur ne porte que contenu et mécanisme, tout habillage
visuel revient à un futur système de templates) — voir aussi le mode SVG
inline de "image" (svg_markup) et la pièce jointe téléchargeable de
"bouton" (attachment_filename), ci-dessous.
MINIGAME_KINDS: tuple[str, ...]
("quiz", "association", "memory", "mots", "scenario", "zones") —
"quiz", "association" et "memory" sont implémentés (voir
quiz_config.py/association_config.py/memory_config.py ci-dessous) ;
les 3 autres gardent un panneau Propriétés minimal (emplacement réservé),
formulaires de contenu dédiés hors périmètre de cette passe.
ELEMENT_LIBRARY: dict[str, dict[str, Any]]
Bibliothèque affichée dans le panneau gauche, groupée par catégorie
(contenu / minigames), chaque entrée portant un label et sa liste
de kinds. "row" n'y apparaît jamais — créé implicitement par le
moteur de layout, jamais choisi directement dans la bibliothèque.
ELEMENT_KIND_LABELS: dict[str, str]
Libellé d'affichage pour chaque kind, y compris "row" (pour
l'affichage/debug hors bibliothèque).
element_default_attributes(kind: str) -> dict[str, Any]
Attributs posés à la création d'un élément de ce type (voir
document_engine/elements/add_document_element.py).
- Retour : un dict d'attributs par défaut, dépendant du
kind: texte (content/style+bold/italic/underline/align/color/max_width—max_widthvide par défaut = pleine largeur de.docPageContent, une valeur CSS libre ex."60ch"/"480px"restreint le bloc, voirrendering.md), image (src/alt/svg_markup—svg_markupprend le pas sursrcau rendu, voir rendering.md), bouton (label/target/ attachment_stored_name/attachment_filename— la pièce jointe est indépendante detarget, réservé à la navigation), liste à puces/numérotée (items, une liste de chaînes), badge (content), carte (label/title/description, contenu pur sans couleur/forme), rangée (gap/align/justify), quiz (DEFAULT_QUIZ_CONFIG, voirquiz_config.py), association (DEFAULT_ASSOCIATION_CONFIG, voirassociation_config.py), memory (DEFAULT_MEMORY_CONFIG, voirmemory_config.py), autre mini-jeu (theme_color), ou{}pour unkindinconnu. - Exceptions : aucune.
quiz_config.py — modèle de données du mini-jeu Quiz
Seul mini-jeu réellement implémenté (les autres MINIGAME_KINDS restent
un emplacement réservé). Même convention resolve_X/sanitize_X que
game_engine/rendering/quiz_box_config.py côté jeu, mais sans aucun
import croisé (voir docs/plan/PLAN.md).
MIN_CHOICES, MAX_CHOICES, MIN_TIMER_SECONDS, MAX_TIMER_SECONDS, DEFAULT_TIMER_SECONDS: int
Bornes de validation (2/4 choix, 5/300 secondes, 30 par défaut).
DEFAULT_QUIZ_CONFIG: dict[str, Any]
{"theme_color": "#ff5f2e", "timer_enabled": False, "timer_seconds": 30, "questions": []}.
sanitize_quiz_config(raw_config: Any) -> dict[str, Any]
Valide/nettoie une config de quiz arbitraire (JSON venu du client) —
jamais lève, renvoie toujours un dict COMPLET fusionné sur
DEFAULT_QUIZ_CONFIG. Chaque question de raw_config["questions"] est
validée indépendamment (voir _sanitize_question, privée) : texte non
vide, au moins 2 choix non vides (tronqués à 4), correct_index remis à
0 s'il est absent/hors bornes/non-entier (un bool — qui est un int en
Python — est explicitement rejeté), points codé en entier ≥ 0. Une
question invalide est silencieusement supprimée de la liste (jamais une
levée qui ferait échouer tout le reste du quiz).
- Retour : dict complet (mêmes clés que
DEFAULT_QUIZ_CONFIG). - Exceptions : aucune.
quiz_total_points(config: dict[str, Any]) -> int
Somme du points de toutes les questions d'une config déjà sanitizée —
mirroir de db/dialogue_lines.py::sum_question_rewards côté jeu, utile
le jour où un export calculera un score maximum.
- Retour : entier ≥ 0.
- Exceptions : aucune.
association_config.py — modèle de données du mini-jeu Association
Deuxième mini-jeu implémenté : l'apprenant relie chaque carte de gauche
("terme") à sa correspondance de droite ("définition") par glisser-déposer
(voir document_engine/rendering/render_document_element.py::
_render_association_player). Même convention que quiz_config.py.
MIN_PAIRS, MAX_PAIRS: int
Bornes de validation (2/8 paires). MIN_PAIRS n'est pas imposé par
sanitize_association_config (une seule paire valide reste acceptée,
comme un quiz à une seule question) — c'est une recommandation pour le
panneau Propriétés, pas une contrainte technique du rendu.
DEFAULT_ASSOCIATION_CONFIG: dict[str, Any]
{"theme_color": "#ff5f2e", "pairs": []}.
sanitize_association_config(raw_config: Any) -> dict[str, Any]
Valide/nettoie une config d'association arbitraire (JSON venu du client)
— jamais ne lève, renvoie toujours un dict COMPLET fusionné sur
DEFAULT_ASSOCIATION_CONFIG. Chaque paire de raw_config["pairs"] est
validée indépendamment (voir _sanitize_pair, privée) : les deux côtés
(left/right) doivent être non vides une fois .strip()-és, sinon la
paire entière est silencieusement supprimée de la liste (jamais une
levée qui ferait échouer tout le reste du mini-jeu). La liste finale est
tronquée à MAX_PAIRS.
- Retour : dict complet (mêmes clés que
DEFAULT_ASSOCIATION_CONFIG). - Exceptions : aucune.
memory_config.py — modèle de données du mini-jeu Memory
Troisième mini-jeu implémenté : l'apprenant retourne des cartes pour
constituer des paires identiques (mode "paire") ou simplement révéler
chaque carte une fois (mode "single", sans appariement — voir
document_engine/rendering/render_document_element.py::
_render_memory_player). Même convention que quiz_config.py.
MIN_CARDS, MAX_CARDS: int
Bornes de validation (2/8 cartes DÉFINIES par le créateur — en mode
"paire", le plateau affiche le double, chaque carte étant dupliquée).
MIN_CARDS n'est pas imposé par sanitize_memory_config (même logique
que MIN_PAIRS côté Association) — recommandation pour le panneau
Propriétés, pas une contrainte technique du rendu.
CARD_MODES: tuple[str, ...]
("paire", "single").
DEFAULT_MEMORY_CONFIG: dict[str, Any]
{"theme_color": "#ff5f2e", "mode": "paire", "cards": []}.
sanitize_memory_config(raw_config: Any) -> dict[str, Any]
Valide/nettoie une config de memory arbitraire (JSON venu du client) —
jamais ne lève, renvoie toujours un dict COMPLET fusionné sur
DEFAULT_MEMORY_CONFIG. mode retombe sur "paire" s'il n'est pas dans
CARD_MODES. Chaque carte de raw_config["cards"] est validée
indépendamment (voir _sanitize_card/_sanitize_card_face, privées) :
chaque face (recto/verso) a un image et un text indépendants et
tous deux optionnels, MAIS le verso doit avoir au moins l'un des deux
non vide (rien à révéler/apparier sinon) — le recto, lui, peut rester
entièrement vide (dos de carte générique "?" par défaut côté rendu). Une
carte invalide est silencieusement supprimée de la liste. La liste finale
est tronquée à MAX_CARDS.
- Retour : dict complet (mêmes clés que
DEFAULT_MEMORY_CONFIG). - Exceptions : aucune.
mots_config.py — modèle de données du mini-jeu Mots mêlés
Quatrième mini-jeu implémenté : l'apprenant retrouve chaque mot caché
dans une grille de lettres (horizontalement, verticalement, ou en
diagonale — voir document_engine/rendering/render_document_element.py::
_render_mots_player, qui construit la grille elle-même). Même
convention que quiz_config.py.
MIN_WORDS, MAX_WORDS: int
Bornes de validation (5/10 mots). MIN_WORDS n'est pas imposé par
sanitize_mots_config (même logique que MIN_PAIRS/MIN_CARDS
côté Association/Memory) — recommandation pour le panneau Propriétés,
pas une contrainte technique du rendu : un seul mot valide reste
accepté, la grille se construit quand même autour de lui.
MIN_WORD_LENGTH, MAX_WORD_LENGTH: int
Bornes de longueur d'un mot individuel une fois nettoyé (2/20
lettres) — un mot trop court n'a pas de sens à chercher, un mot trop
long compliquerait inutilement le calcul de la taille de la grille
(voir _mots_grid_size_for_words dans render_document_element.py).
DEFAULT_MOTS_CONFIG: dict[str, Any]
{"theme_color": "#ff5f2e", "words": []}.
sanitize_mots_config(raw_config: Any) -> dict[str, Any]
Valide/nettoie une config de mots mêlés arbitraire (JSON venu du
client) — jamais ne lève, renvoie toujours un dict COMPLET fusionné sur
DEFAULT_MOTS_CONFIG. Chaque mot de raw_config["words"] est nettoyé
indépendamment (voir _sanitize_word, privée) : mis en MAJUSCULES,
les accents sont retirés (décomposition NFKD + filtrage ASCII, pour que
deux mots qui se croisent sur une même case de la grille puissent
partager exactement la même lettre), seules les lettres sont
conservées. Un mot qui ne contient plus rien d'exploitable (ou moins de
MIN_WORD_LENGTH lettres) une fois nettoyé est silencieusement
supprimé de la liste, de même qu'un doublon exact d'un mot déjà retenu
(un mot répété deux fois n'aurait rien de plus à faire trouver). La
liste finale est tronquée à MAX_WORDS.
- Retour : dict complet (mêmes clés que
DEFAULT_MOTS_CONFIG). - Exceptions : aucune.
scenario_config.py — modèle de données du mini-jeu Scénario
Cinquième mini-jeu implémenté : l'apprenant lit une situation initiale,
choisit une option, puis découvre la conséquence de SON choix — cette
conséquence peut elle-même mener à de nouveaux choix, et ainsi de suite
(arbre de décision, voir document_engine/rendering/ render_document_element.py::_render_scenario_player). Aucune notion
de bonne/mauvaise réponse (retour utilisateur du 20/09/2026 : "il n'y a
pas de notion vrai/faux, l'utilisateur observe les conséquences") —
l'apprenant explore simplement les branches jusqu'à une fin. Le
créateur peut définir plusieurs scénarios (plusieurs arbres
indépendants), joués les uns après les autres dans l'ORDRE d'écriture
(jamais mélangés, contrairement à Association/Memory/Mots mêlés). Même
convention resolve_X/sanitize_X que quiz_config.py.
Structure d'un scénario : {"title": str, "nodes": [...]}.
nodes[0] est TOUJOURS la situation initiale (racine de l'arbre, jamais
supprimable depuis le panneau Propriétés — voir
forgeDocScenarioGraphRenderInspector, static/document/js/
document-editor.js). Chaque nœud est {"id": str, "text": str, "x": float, "y": float, "choices": [...]} — id est une référence
STABLE (générée côté client, jamais recalculée côté serveur) vers
laquelle un choix d'un AUTRE nœud peut pointer via son target_id ;
x/y positionnent le nœud sur le canevas du graphe visuel (voir
forgeDocRenderScenarioTreeModal) ; un nœud SANS choix est une fin de
branche (état normal, pas une erreur — contrairement à l'ancien modèle
qui exigeait 2 à 4 choix par scénario). Un choix est {"text": str, "target_id": str | None} — target_id à None signifie "fin de
branche à ce choix".
MAX_SCENARIO_CHOICES: int
Borne technique du nombre de choix par nœud (4) — évite une UI
disproportionnée, aucun lien avec une notion de bonne/mauvaise réponse.
Nommée explicitement "SCENARIO" (pas juste MAX_CHOICES) pour éviter
toute collision avec la constante de même nom de quiz_config.py, qui
borne un concept différent (le nombre de réponses d'une question de
quiz) — document_engine/__init__.py ré-exporte l'intégralité de l'API
publique du paquet à plat, un nom générique entrerait en collision.
Contrairement à l'ancien modèle, il n'existe PAS de minimum : un nœud
peut avoir 0 choix (fin de branche), c'est un état valide, pas une
erreur à filtrer.
DEFAULT_SCENARIO_CONFIG: dict[str, Any]
{"theme_color": "#ff5f2e", "scenarios": []}.
sanitize_scenario_config(raw_config: Any) -> dict[str, Any]
Valide/nettoie une config de scénario arbitraire (JSON venu du client) —
jamais ne lève, renvoie toujours un dict COMPLET fusionné sur
DEFAULT_SCENARIO_CONFIG. Chaque scénario de raw_config["scenarios"]
est validé indépendamment (voir _sanitize_scenario/
_sanitize_scenario_node/_sanitize_scenario_choice, privées) : le
title doit être non vide, chaque nœud doit avoir un id (chaîne non
vide) et un text non vide une fois .strip()-é (sinon le nœud entier
est supprimé), et ses choix sont tronqués à MAX_SCENARIO_CHOICES. Un
x/y manquant ou invalide (pas un nombre, ou un booléen — bool
hérite de int en Python) retombe sur un quadrillage en cascade dérivé
de l'INDEX du nœud dans la liste (_scenario_node_fallback_position),
jamais (0, 0) pour tous les nœuds, ce qui les empilerait exactement au
même endroit sur le graphe visuel. Le scénario entier est supprimé s'il
ne reste plus aucun nœud valide après
nettoyage (il faut au moins la situation initiale). Une fois l'ensemble
des ids valides connu, tout target_id qui ne pointe plus vers un nœud
existant (nœud invalide/supprimé) est silencieusement remis à None
("fin de branche") plutôt que de faire échouer tout le scénario — même
philosophie que le reste de ce module : ne jamais faire échouer une
structure entière pour une seule référence cassée.
- Retour : dict complet (mêmes clés que
DEFAULT_SCENARIO_CONFIG). - Exceptions : aucune.
sanitize_element_attributes.py — point d'entrée unique de revalidation par kind
- Dispatch
kind -> sanitize_X_config(_SANITIZERS, privée) pour les 5 - mini-jeux à structure garantie (Quiz/Association/Memory/Mots
- mêlés/Scénario) — les autres kinds n'ont pas de sanitizer et sont
- renvoyés tels quels. Utilisé à la fois à l'ÉCRITURE
- (
routes/document/document_element_update.py) ET à la LECTURE - (
routes/document/document_edit.py,routes/document/document_render.py) - sans ce second usage, des attributs stockés dans un schéma devenu
obsolète (ex. Scénario, passé d'une liste plate à un arbre de décision)
atteindraient le panneau Propriétés côté client TELS QUELS, qui suppose
la forme ACTUELLE — bug réel constaté le 21/09/2026, un élément Scénario
créé avant la refonte en arbre faisait planter silencieusement le
panneau Propriétés (
scenario.nodesinexistant sur l'ancienne forme).
sanitize_element_attributes(kind: str, attributes: Any) -> Any
- Retour :
attributesrevalidé sikinda un sanitizer, sinonattributestel quel. - Exceptions : aucune (délègue à des sanitizers qui ne lèvent jamais).