Ajoute la section Après le GONG, les responsables de Mé 67 et 15 actualités vidéo
- Nouvelle section /apres-le-gong/ : 7 pages (contexte, organisations, personnes, chronologie 1968-2025, événements majeurs, culture et héritage, rapport de vérification) + schéma des filiations. - Nouvelle page /responsables/ : Pierre Bolotte et Jacques Foccart, la chaîne de commandement de la répression de mai 1967. - Actualités : chaque vidéo YouTube devient une actualité datée de sa publication (15 articles, 2013-2026), avec miniatures auto-hébergées (CSP 'self') servant aussi de couvertures. - Helper markdown partagé : rendu des références [^N^] en appels de note cliquables + section Sources ancrée. - Dates partielles (année ou mois seul) affichées sans jour inventé ; parsing des dates corrigé contre le décalage de fuseau horaire. - Maillage : nav, footer, accueil, liens depuis /me-67/ et /heritage/, sitemap, README.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// Helpers markdown partagés (côté serveur uniquement, exécutés au build).
|
||||
// - parseFrontmatter : même logique que src/lib/server/actualites.ts.
|
||||
// - renderAvecSources : rend le corps en HTML en traitant les notes de
|
||||
// référence au format [^67^] utilisé par les fichiers sources :
|
||||
// appels inline → <sup> avec ancre #src-67, définitions regroupées en
|
||||
// section « Sources » finale (<ol class="sources">).
|
||||
import { marked } from 'marked';
|
||||
|
||||
/** Parse un front-matter YAML simple (clés scalaires, valeurs entre guillemets optionnelles). */
|
||||
export function parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {
|
||||
const m = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
||||
if (!m) return { data: {}, body: raw };
|
||||
const data: Record<string, string> = {};
|
||||
for (const line of m[1].split(/\r?\n/)) {
|
||||
const kv = line.match(/^([A-Za-z0-9_]+)\s*:\s*(.*)$/);
|
||||
if (!kv) continue;
|
||||
let val = kv[2].trim();
|
||||
if (
|
||||
(val.startsWith('"') && val.endsWith('"')) ||
|
||||
(val.startsWith("'") && val.endsWith("'"))
|
||||
) {
|
||||
val = val.slice(1, -1);
|
||||
}
|
||||
data[kv[1]] = val;
|
||||
}
|
||||
return { data, body: m[2] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Rend un corps markdown en HTML.
|
||||
* - Les définitions `[^N^]: https://...` sont retirées du corps et deviennent
|
||||
* une section finale « Sources » (<h2 id="sources"> + <ol class="sources">).
|
||||
* - Les appels inline `[^N^]` deviennent <sup class="ref"><a href="#src-N">[N]</a></sup>.
|
||||
* - Un appel sans définition est laissé en texte brut `[N]` (pas d'ancre morte :
|
||||
* le prérendu échouerait sinon, handleMissingId: 'fail').
|
||||
*/
|
||||
export function renderAvecSources(body: string): string {
|
||||
const sources = new Map<number, string>();
|
||||
|
||||
// 1. Extraction des définitions de notes (en fin de fichier).
|
||||
const corps = body.replace(/^\[\^(\d+)\^\]:\s*(https?:\/\/\S+)\s*$/gm, (_all, n: string, url: string) => {
|
||||
sources.set(Number(n), url);
|
||||
return '';
|
||||
});
|
||||
|
||||
// 2. Remplacement des appels inline.
|
||||
const avecRefs = corps.replace(/\[\^(\d+)\^\]/g, (all, n: string) => {
|
||||
if (!sources.has(Number(n))) return `[${n}]`;
|
||||
return `<sup class="ref"><a href="#src-${n}">[${n}]</a></sup>`;
|
||||
});
|
||||
|
||||
// 3. Section « Sources » finale (HTML brut, repris tel quel par marked).
|
||||
let html = marked.parse(avecRefs.trim(), { async: false }) as string;
|
||||
if (sources.size) {
|
||||
const items = [...sources.entries()]
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(
|
||||
([n, url]) =>
|
||||
`<li id="src-${n}"><a href="${url}" rel="external noopener noreferrer" target="_blank">${url}</a></li>`
|
||||
)
|
||||
.join('\n');
|
||||
html += `\n<h2 id="sources">Sources</h2>\n<ol class="sources">\n${items}\n</ol>`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
Reference in New Issue
Block a user