diff --git a/app/src/components/HtmlEmbed.astro b/app/src/components/HtmlEmbed.astro
index 7d53a9b5c4a3e1f5432f83aa7b3a639d45fa6481..a8f1667eb27e43eba9b2638057b6abe6eaea602a 100644
--- a/app/src/components/HtmlEmbed.astro
+++ b/app/src/components/HtmlEmbed.astro
@@ -6,13 +6,43 @@ const { src, title, desc, frameless = false, align = 'left' } = Astro.props as P
const embeds = (import.meta as any).glob('../content/embeds/**/*.html', { query: '?raw', import: 'default', eager: true }) as Record;
function resolveFragment(requested: string): string | null {
- // Allow both "banner.html" and "embeds/banner.html"
- const needle = requested.replace(/^\/*/, '');
- for (const [key, html] of Object.entries(embeds)) {
- if (key.endsWith('/' + needle) || key.endsWith('/' + needle.replace(/^embeds\//, ''))) {
- return html;
+ // Résolution tolérante: accepte avec/sans "embeds/", avec/sans ".html", insensible à la casse, et recherche par basename
+ const norm = (s: string) => String(s || '')
+ .trim()
+ .replace(/^\/*/, '')
+ .replace(/^\.\//, '')
+ .toLowerCase();
+ const raw = norm(requested);
+ const withHtml = raw.endsWith('.html') ? raw : `${raw}.html`;
+ const stripEmbeds = (s: string) => s.replace(/^embeds\//, '');
+ const v1 = raw;
+ const v2 = withHtml;
+ const v3 = stripEmbeds(v1);
+ const v4 = stripEmbeds(v2);
+ const needles = new Set([v1, v2, v3, v4]);
+
+ // Construire table des chemins normalisés -> contenu
+ const entries = Object.entries(embeds).map(([key, html]) => {
+ const nk = norm(key);
+ const base = nk.split('/').pop() || nk;
+ return { nk, base, html };
+ });
+
+ // 1) Correspondance par fin de chemin complète
+ for (const e of entries) {
+ for (const v of needles) {
+ if (e.nk.endsWith('/' + v) || e.nk === v) return e.html;
+ }
+ }
+
+ // 2) Correspondance par basename
+ for (const e of entries) {
+ for (const v of needles) {
+ const vb = v.split('/').pop();
+ if (e.base === vb) return e.html;
}
}
+
return null;
}
@@ -69,7 +99,7 @@ const mountId = `frag-${Math.random().toString(36).slice(2)}`;
+
+