import json # Widget d'interface "💬 Boîte de dialogue" (voir "🖥️ Interface", # templates/scene_edit.html, screens/scenes/add_scene_object.py kind= # "dialogue_box") : affiche AUTOMATIQUEMENT, en jeu, la réplique en cours # d'une quête déclenchée par une règle de collision "quete" (voir # static/js/play/dialogue-box-controller.js) — header (nom du # personnage qui parle), corps (texte), pied (bouton "Suivant"), # entièrement stylisable par l'auteur. DEFAULT_DIALOGUE_BOX_STYLE = { "font_family": "inherit", "font_size": 14, "font_weight": "normal", "text_color": "#ffffff", "header_bg": "#2f6fed", "body_bg": "#1a1d24", "footer_bg": "#11141b", } FONT_WEIGHT_CHOICES = ["normal", "bold"] def sanitize_dialogue_box_style(raw_style): style = dict(DEFAULT_DIALOGUE_BOX_STYLE) if not isinstance(raw_style, dict): return style if isinstance(raw_style.get("font_family"), str) and raw_style["font_family"].strip(): style["font_family"] = raw_style["font_family"].strip()[:60] try: style["font_size"] = max(8, min(48, int(raw_style.get("font_size")))) except (TypeError, ValueError): pass if raw_style.get("font_weight") in FONT_WEIGHT_CHOICES: style["font_weight"] = raw_style["font_weight"] for key in ("text_color", "header_bg", "body_bg", "footer_bg"): value = raw_style.get(key) if isinstance(value, str) and value.strip(): style[key] = value.strip()[:20] return style def resolve_dialogue_box_style(obj): raw = (obj.get("attributes") or {}).get("_dialogue_box_style") if not raw: return dict(DEFAULT_DIALOGUE_BOX_STYLE) try: parsed = json.loads(raw) except (TypeError, ValueError): return dict(DEFAULT_DIALOGUE_BOX_STYLE) return sanitize_dialogue_box_style(parsed)