From b60fe63a542511c95153427f270c5b5923e5322c Mon Sep 17 00:00:00 2001 From: william Date: Wed, 2 Sep 2026 10:12:44 +0200 Subject: [PATCH] =?UTF-8?q?Export=20Web/SCORM=20:=20corrige=20le=20chemin?= =?UTF-8?q?=20de=20l'ic=C3=B4ne=20doubl=C3=A9=20"static/static/"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le premier correctif ("static/icons/x.svg") ne suffisait pas : cette URL est posée en style inline (--icon-url) mais CONSOMMÉE par `mask-image: var(--icon-url)` dans static/style.css (.icon-svg) — une URL relative dans une propriété personnalisée CSS se résout par rapport à la feuille de style où le var() est UTILISÉ, pas où elle est définie (piège CSS connu). Résultat : "static/icons/x.svg" redevenait "static/static/icons/x.svg" une fois résolu depuis static/style.css (CORS bloqué — deuxième rapport du même utilisateur). Seul "icons/x.svg" (sans le préfixe "static/") est correct une fois résolu depuis static/style.css. Corrigé côté rendu initial (_relativize_absolute_urls) et côté port JS (forgeRenderIcone, qui régénère ce widget après une action). Co-Authored-By: Claude Sonnet 5 --- publish/build_scorm_package.py | 14 +++++++++++++- .../__tests__/render-special-widgets.test.js | 2 +- .../js/play/offline/render-special-widgets.js | 18 +++++++++++------- tests/test_export_scorm.py | 16 ++++++++++++---- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/publish/build_scorm_package.py b/publish/build_scorm_package.py index 76c9cb32..5ada3bb5 100644 --- a/publish/build_scorm_package.py +++ b/publish/build_scorm_package.py @@ -67,9 +67,21 @@ def _relativize_absolute_urls(html, slug): ailleurs que dans une URL réellement générée par le moteur. Sans ce correctif, un index.html ouvert en local (file://) échouait à charger icônes/sprites/fichiers envoyés (CORS bloque une requête absolue vers - file:///static/... — "la requête CORS n'utilise pas HTTP").""" + file:///static/... — "la requête CORS n'utilise pas HTTP"). + + Cas particulier des icônes (render_icone.py) : leur URL est posée dans + un style INLINE (--icon-url), mais CONSUMÉE par `mask-image: + var(--icon-url)` dans static/style.css (voir .icon-svg) — une URL + relative dans une PROPRIÉTÉ PERSONNALISÉE CSS se résout par rapport à + la feuille de style où le var() est UTILISÉ, pas où elle est définie + (piège CSS connu). "icons/x.svg" (juste "/static/" retiré, sans le + garder) reste donc correct une fois résolu depuis static/style.css, + alors que "static/icons/x.svg" y redevient "static/static/icons/x.svg" + (bug signalé — CORS bloqué sur ce chemin doublé). Traité EN PREMIER, + plus spécifique que le remplacement générique juste après.""" return ( html.replace(f"/game/{slug}/uploads/", "uploads/") + .replace("/static/icons/", "icons/") .replace("/static/", "static/") ) diff --git a/static/js/play/offline/__tests__/render-special-widgets.test.js b/static/js/play/offline/__tests__/render-special-widgets.test.js index 55872594..ac04a7ed 100644 --- a/static/js/play/offline/__tests__/render-special-widgets.test.js +++ b/static/js/play/offline/__tests__/render-special-widgets.test.js @@ -55,7 +55,7 @@ test('icône — repli sur "star" si _icon_slug absent', () => { const html = forgeRenderElementHtml(el, baseGameData(), {}, {}); // le style entier passe par forgeHtmlEscape (comme html.escape() côté // serveur) : l'apostrophe de url('...') devient '. - assert.ok(html.includes("--icon-url:url('static/icons/star.svg')")); + assert.ok(html.includes("--icon-url:url('icons/star.svg')")); }); test('onglets — un panneau actif, les autres masqués', () => { diff --git a/static/js/play/offline/render-special-widgets.js b/static/js/play/offline/render-special-widgets.js index e82f5762..d2c54cea 100644 --- a/static/js/play/offline/render-special-widgets.js +++ b/static/js/play/offline/render-special-widgets.js @@ -73,13 +73,17 @@ function forgeRenderIcone(el, meta, gameData, childrenMap, ctx, parentFlexDirect var existingClass = attrs.class || ""; delete attrs.class; attrs.class = (existingClass + " icon-svg").trim(); - // Chemin RELATIF (jamais "/static/...") : ce fichier ne s'exécute que - // dans le paquet exporté (window.FORGE_OFFLINE, voir render-element.js), - // ouvert en local (file://) sans racine de serveur — un chemin absolu y - // déclenche un blocage CORS ("la requête CORS n'utilise pas HTTP"), - // exactement le bug qui affectait aussi le rendu initial côté serveur - // (voir _relativize_absolute_urls dans publish/build_scorm_package.py). - var iconUrl = "static/icons/" + iconSlug + ".svg"; + // Chemin RELATIF, et surtout PAS préfixé "static/" : cette URL est + // posée dans --icon-url (style inline) mais CONSOMMÉE par `mask-image: + // var(--icon-url)` dans static/style.css (voir .icon-svg) — une URL + // relative dans une propriété personnalisée CSS se résout par rapport à + // LA FEUILLE DE STYLE où le var() est utilisé (static/style.css), pas + // où elle est définie (piège CSS connu) : "static/icons/x.svg" y + // redeviendrait "static/static/icons/x.svg" (404/CORS bloqué — bug + // signalé). "icons/x.svg" est correct une fois résolu depuis + // static/style.css (voir aussi _relativize_absolute_urls dans + // publish/build_scorm_package.py, même correctif côté rendu initial). + var iconUrl = "icons/" + iconSlug + ".svg"; var style = forgeStyleString(el, parentFlexDirection, el.widget) + " --icon-url:url('" + iconUrl + "');"; return ""; } diff --git a/tests/test_export_scorm.py b/tests/test_export_scorm.py index 52874d8d..a9f2f51c 100644 --- a/tests/test_export_scorm.py +++ b/tests/test_export_scorm.py @@ -56,9 +56,16 @@ def test_export_scorm_icon_widget_uses_a_relative_url(client, game): part de la racine du serveur Flask) — jamais réécrit par le refactor asset_url de templates/play.html (celui-ci ne touche que les