# document_engine/rendering/ Rendu HTML du document — point d'entrée unique utilisé à la fois par le canevas d'édition et par le Mode Aperçu (même fonction, voir `docs/plan/PLAN.md` — "Aperçu réel"). ## `render_document(elements: list[dict[str, Any]]) -> str` Assemble le document entier à partir de la liste à plat renvoyée par `document_engine.list_document_elements` : regroupe les éléments par `parent_id`, puis rend récursivement les éléments top-niveau dans l'ordre (une rangée rend elle-même ses propres enfants côte à côte). - **Retour** : le HTML complet du document. - **Exceptions** : aucune. ## `render_document_element(el: dict[str, Any], children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str` HTML d'un seul élément — dispatch par dict (table `_RENDERERS`) selon `el["kind"]`, plutôt qu'un enchaînement de `if` (mirroir de l'esprit de `game_engine/scenes/render_scene_object.py`). `children_by_parent` est le regroupement précalculé par `render_document`, transmis pour que les rangées puissent rendre récursivement leurs enfants sans refaire le regroupement à chaque appel. - **Retour** : le HTML de cet élément (et de ses enfants s'il s'agit d'une rangée). - **Exceptions** : aucune ; un `kind` inconnu produit un bloc `docUnknown` visible plutôt qu'une levée d'exception. ### Détail des rendus par catégorie (fonctions privées, table de dispatch) - **Rangée** (`row`) : conteneur flex (`gap`/`align-items`/ `justify-content` réels depuis `attributes`), enfants rendus récursivement. - **Texte** (`titre`/`paragraphe`) : `
` stylé selon `style` (préréglage taille/graisse/interligne) et `bold`/`italic`/`underline`/`strikethrough`/ `align`/`color`. `underline`/`strikethrough` se combinent dans un seul `text-decoration` (`"underline line-through"` si les deux sont actifs). `font_size`/`line_height` (vides par défaut) remplacent les valeurs du préréglage `style` SANS toucher `font-weight` (toujours piloté par le préréglage + `bold`). `text_transform` (`"none"` par défaut) ajoute `text-transform` quand différent de `"none"`. `font_family`/ `letter_spacing`/`text_shadow` (vides par défaut) ajoutent leur déclaration CSS respective quand non vides. `max_width` (optionnel, ex. `"60ch"`, `"480px"`) ajoute `max-width` au style inline quand non vide — pleine largeur de `.docPageContent` par défaut, retour utilisateur du 24/09/2026 (un paragraphe doit pouvoir rester plus étroit que la page, sans dépendre d'une rangée qui en partagerait la largeur avec un frère). Termine par `render_box_style(a)` (voir `box_style.py` ci-dessous) pour `padding`/`margin`/`background_color`/`border_radius`/`border`/ `align_self` — attributs PARTAGÉS avec d'autres kinds, jamais dupliqués ici (audit du 26/09/2026, réglages manquants à couvrir élément par élément en réutilisant ce module). - **Image** : ``, ou un bloc placeholder si `src` est vide — OU, si `attributes["svg_markup"]` est non vide (prioritaire sur `src`), un `
` portant directement ce fragment SVG nettoyé par `sanitize_svg_markup` (voir `sanitize_svg_markup.py` ci-dessous) : un contenu vectoriel dessiné/collé par le créateur plutôt qu'un fichier hébergé. Style inline : `object_fit` (`"cover"`/`"contain"`/`"fill"`, toute autre valeur ignorée), `aspect_ratio` (valeur CSS libre, ex. `"16 / 9"`), `filter_preset` (`"grayscale"`/`"sepia"`/`"blur"`, mappé vers une vraie valeur `filter` CSS fixe — jamais une valeur de filtre libre) + les attributs de boîte partagés (`render_box_style`, voir `box_style.py`). `lazy_load` (`True`) ajoute `loading="lazy"` sur l'`` uniquement (comportement, pas du style). `click_behavior` (`""`/`"link"`/`"lightbox"`) enveloppe le tout dans un `` (si `link_url` est aussi renseigné) ou un `
` — les deux ne deviennent réellement cliquables qu'en Mode Aperçu (voir static/document/js/ document-editor.js::forgeDocBindCanvasInteractions/ forgeDocOpenImageLightbox), même principe que les mini-jeux et la pièce jointe d'un bouton. `caption` (non vide) enveloppe le tout dans un `
` échappée. - **Bouton** : `