{# Carte d'une présentation Slidia (bibliothèque) — même coque que les cartes Moodia et Scormia, désormais partagée via _organisms/entity-card : liseré d'accent au survol, corps, pied daté, emplacement du menu contextuel. Slidia ne fournit que son contenu. Requiert : presentation, slideCount, sdColor. CONTRAT DU PARTAGE PUBLIC — LIRE AVANT DE TOUCHER AU CONTENEUR : le bouton « Partager » ne vit PAS dans cette carte mais dans le menu contextuel PARTAGÉ de la page (.sd-menu, cf. index.html.twig). shared--share-link#writeBack() retrouve donc la carte par `document.querySelector('.sdl-pres-card[data-uuid="…"]')`, puis y réécrit data-shared / data-share-download / data-share-url et bascule [data-share-badge]. La classe `sdl-pres-card` et ces quatre attributs de données forment un contrat : les renommer casse le partage EN SILENCE. Ces attributs passent par le paramètre `attr` d'entity-card (attributs bruts, jamais des -param Stimulus : un -param JSON-parserait "1"/"0" en nombres et casserait les comparaisons === '1' de list_controller.js#toggleMenu et de share_link_controller.js). #} {% set templateName = presentation.template is not null ? presentation.template.name : 'Sans masque' %} {# ── Métriques du corps de carte, sur le modèle de scormia/_module_card.html.twig ── Scormia annonce « N parties · N écrans » puis une rangée de méta (quiz, réussite, langue). Slidia n'a pas ces dimensions : ses faits vérifiables sont le nombre de slides, les notes d'intervenant, l'ORIGINE de la présentation et le profil de génération. Tous sont lus sur l'entité ou sur `slidesData` — rien n'est inventé. `s.notes|default('')` et non `s.notes` : sous `strict_variables` (activé en test), une clé absente d'un tableau lève une erreur, et `notes` n'existe que sur les slides produites par l'IA. Le filtre `default` est compilé spécialement par Twig et tolère l'accès manquant — c'est son emploi normal. #} {% set slides = presentation.slidesData ?? [] %} {% set notesCount = slides|filter(s => s.notes|default('')|trim is not empty)|length %} {# L'origine se DÉDUIT, elle n'est pas stockée : `sourceHash` n'est renseigné que par l'import de document (SlidiaController::createSubmit), et `brief` que par la saisie. Une présentation sans l'un ni l'autre vient de « Rédiger manuellement ». #} {% set origin = presentation.sourceHash ? { label: 'Depuis un document', icon: 'document' } : (presentation.brief ? { label: 'Depuis un brief', icon: 'magic-stick' } : { label: 'Rédaction manuelle', icon: 'edit' }) %} {# TOUT ce que les blocs ci-dessous utilisent doit figurer dans le `with {}` : l'embed est rendu sous `only`, les blocs ne voient donc QUE ce contexte-là — jamais les variables posées plus haut dans ce fichier. En omettre une lève une RuntimeError sous `strict_variables`, soit un 500 sur la bibliothèque entière. (Et pas de commentaire Twig À L'INTÉRIEUR du `with {}` : le lexer n'y attend qu'une expression, un `{#` y produit une SyntaxError que `lint:twig` signale, mais seulement si on le relance après coup.) #} {% embed '_organisms/entity-card/entity-card.html.twig' with { href: presentation.uuid ? path('app_slidia_editor', {uuid: presentation.uuid}) : '#', accent: sdColor.primary, class: 'sdl-pres-card', attr: { 'data-title': presentation.title|lower, 'data-presentation-id': presentation.id, 'data-template-id': presentation.template ? presentation.template.id : '', 'data-uuid': presentation.uuid, 'data-shared': presentation.isShared ? '1' : '0', 'data-share-download': presentation.isShareDownloadEnabled ? '1' : '0', 'data-share-url': presentation.isShared ? url('app_public_slidia_present', {token: presentation.shareToken}) : '' }, presentation: presentation, slideCount: slideCount, templateName: templateName, notesCount: notesCount, origin: origin } only %} {# Pastille du masque, suivie du tag « Partagé ». Les deux restent à gauche, jamais alignés à droite : le menu ⋯ (absolute top-3 right-3) passerait par-dessus. Le tag est TOUJOURS rendu (pas de {% if %}) : shared--share-link#save()/revoke() bascule son display sans recharger la page — le rendre conditionnellement empêcherait toute activation à chaud. Gris neutre volontaire (variante du badge) : il ne doit pas concurrencer la couleur du masque. #} {% block badge %}
{# min-w-0 + truncate (et non une largeur maximale en pourcentage) : un nom de masque long doit se tronquer LUI-MÊME plutôt que de pousser le tag « Partagé » hors de la carte, que overflow-hidden rognerait en silence. #} {{ templateName }} {% include '_atoms/badge/badge.html.twig' with {label: 'Partagé', icon: 'share', variant: 'neutral', size: 'sm', rounded: 'lg'} only %}
{% endblock %} {# Titre (hauteur fixe pour 2 lignes, comme Moodia et Scormia), puis les statistiques et la rangée de méta — même découpage que scormia/_module_card.html.twig, dont le corps de carte n'est pas vide. Plus un : la coque entity-card est déjà un lien, un imbriqué serait invalide. list_controller.js (renommage, toggleMenu) ne lit et n'écrit que le textContent du titre. #} {% block body %}

{{ presentation.title }}

{# UNE seule ligne pour tous les faits de la carte : nombre de slides, slides commentées, origine, profil de génération, mode fidèle. Scormia les répartit sur deux rangées (statistiques puis méta) ; ici tout tient sur une ligne, ce qui évite un grand vide entre deux blocs très courts. `flex-wrap` et non une ligne rigide : sur une carte étroite — trois colonnes en 1280 px, ou l'affichage mobile — les éléments passent à la ligne au lieu d'être rognés par l'`overflow-hidden` de la coque. Le profil et le mode fidèle n'ont de sens que sur une présentation passée par l'IA : sur une rédaction manuelle, le profil enregistré est la valeur par défaut de l'entité, qui n'a jamais servi — l'afficher serait une information fausse. `tokensUsed` est le marqueur le plus sûr d'un passage réel par la génération. #}
{# Les deux premiers faits portent leur icône comme les suivants : sans elle, la ligne commençait par deux nombres nus puis se poursuivait par des éléments illustrés, et le regard ne trouvait pas d'alignement. #} {% include '_atoms/icon/icon.html.twig' with { name: 'presentation-graph', size: 'sm', color: 'gray' } only %} {{ slideCount }} slide{{ slideCount > 1 ? 's' : '' }} {% if notesCount %} {% include '_atoms/icon/icon.html.twig' with { name: 'message', size: 'sm', color: 'gray' } only %} {{ notesCount }} avec notes {% endif %} {% include '_atoms/icon/icon.html.twig' with { name: origin.icon, size: 'sm', color: 'gray' } only %} {{ origin.label }} {% if presentation.fidelityMode %} {% include '_atoms/icon/icon.html.twig' with { name: 'check-circle', size: 'sm', color: 'gray' } only %} Mode fidèle {% endif %}
{% endblock %} {# Date seule, comme le pied de carte de Scormia : le nombre de slides est remonté dans les statistiques du corps. La classe `.font-mono` qui vivait ici portait autrefois un contrat avec list_controller.js#duplicatePresentation, qui clonait la carte et y réécrivait « N slides · À l'instant ». Ce clonage a été remplacé par un rechargement de page (il produisait un doublon silencieux sur une grille pleine) : plus aucun JavaScript ne vise ce nœud, et le contrat n'existe plus. #} {% block footerLeft %} {{ presentation.updatedAt | relative_date }} {% endblock %} {# Consommation IA — tokens à l'accent de l'outil, énergie en ambre (identique à Moodia et Scormia). #} {% block footerRight %} {% if presentation.tokensUsed %} {% include '_atoms/icon/icon.html.twig' with {name: 'token', size: 'xs', color: 'current'} only %} {{ presentation.tokensUsed|format_tokens }} {% endif %} {% if presentation.energyConsumptionWh %} {% include '_atoms/icon/icon.html.twig' with {name: 'bolt', size: 'xs', color: 'warning'} only %} {{ presentation.energyConsumptionWh|format_energy }} {% endif %} {% endblock %} {# Déclencheur du menu contextuel PARTAGÉ de la page (positionné par list_controller.js#toggleMenu depuis ce bouton), hors du
pour ne pas naviguer au clic. Reste un descendant de .sdl-pres-card : toggleMenu() remonte à la carte par closest() pour en recopier le jeu de données sur l'entrée « Partager ». #} {% block menu %}
{% endblock %} {% endembed %}