1

Récupération d'articles d'archives

This commit is contained in:
2026-03-29 03:00:47 +02:00
parent 060119c91c
commit ecc25fe7cf
592 changed files with 17498 additions and 8153 deletions

View File

@@ -1,5 +1,8 @@
"use strict";
const HIGHLIGHT_START = "__MEILI_HIGHLIGHT_START__";
const HIGHLIGHT_END = "__MEILI_HIGHLIGHT_END__";
/**
* Lit la configuration exposee par le template Hugo.
* @param {HTMLElement} root Element principal de la page.
@@ -67,14 +70,27 @@ function readSearchQuery(queryParam) {
* @param {string} query Texte recherche.
* @param {number} limit Nombre de resultats par lot.
* @param {number} offset Position de depart.
* @returns {{q: string, limit: number, offset: number, attributesToRetrieve: string[]}}
* @returns {object}
*/
function buildSearchPayload(query, limit, offset) {
return {
q: query,
limit,
offset,
attributesToRetrieve: ["title", "path", "published_at"],
attributesToRetrieve: [
"title",
"description",
"content",
"path",
"section",
"published_at",
],
attributesToHighlight: ["title", "description", "content"],
attributesToCrop: ["content"],
cropLength: 24,
cropMarker: "...",
highlightPreTag: HIGHLIGHT_START,
highlightPostTag: HIGHLIGHT_END,
};
}
@@ -179,7 +195,7 @@ function setSectionVisibility(section, visible) {
/**
* Convertit un hit brut en resultat exploitable.
* @param {any} hit Resultat brut.
* @returns {{title: string, path: string, published_at: string} | null}
* @returns {{title: string, titleMarkup: string, summary: string, summaryMarkup: string, path: string, section: string, published_at: string} | null}
*/
function normalizeHit(hit) {
if (typeof hit !== "object" || hit === null) {
@@ -194,19 +210,110 @@ function normalizeHit(hit) {
if (typeof hit.title === "string" && hit.title.trim().length > 0) {
title = hit.title.trim();
}
const titleMarkup = readFormattedField(hit, "title") || title;
let publishedAt = "";
if (typeof hit.published_at === "string" && hit.published_at.trim().length > 0) {
publishedAt = hit.published_at.trim();
}
let section = "";
if (typeof hit.section === "string" && hit.section.trim().length > 0) {
section = hit.section.trim();
}
const summaryMarkup = selectSummaryMarkup(hit);
const summary = stripHighlightMarkers(summaryMarkup);
return {
title,
titleMarkup,
summary,
summaryMarkup,
path: hit.path.trim(),
section,
published_at: publishedAt,
};
}
/**
* Lit un champ formate par Meilisearch.
* @param {any} hit Resultat brut.
* @param {string} attribute Nom de l'attribut recherche.
* @returns {string}
*/
function readFormattedField(hit, attribute) {
if (typeof hit !== "object" || hit === null) {
return "";
}
const formatted = hit._formatted;
if (typeof formatted !== "object" || formatted === null) {
return "";
}
const rawValue = formatted[attribute];
if (typeof rawValue !== "string") {
return "";
}
return rawValue.trim();
}
/**
* Indique si une chaine contient des marqueurs de surlignage.
* @param {string} value Texte formate.
* @returns {boolean}
*/
function containsHighlightMarkers(value) {
return value.includes(HIGHLIGHT_START) && value.includes(HIGHLIGHT_END);
}
/**
* Selectionne le meilleur extrait a afficher pour un resultat.
* @param {any} hit Resultat brut.
* @returns {string}
*/
function selectSummaryMarkup(hit) {
const formattedDescription = readFormattedField(hit, "description");
const formattedContent = readFormattedField(hit, "content");
if (containsHighlightMarkers(formattedDescription)) {
return formattedDescription;
}
if (containsHighlightMarkers(formattedContent)) {
return formattedContent;
}
if (typeof hit.description === "string" && hit.description.trim().length > 0) {
return hit.description.trim();
}
if (formattedDescription.length > 0) {
return formattedDescription;
}
if (formattedContent.length > 0) {
return formattedContent;
}
if (typeof hit.content === "string" && hit.content.trim().length > 0) {
return hit.content.trim();
}
return "";
}
/**
* Retire les marqueurs de surlignage d'un texte.
* @param {string} value Texte formate.
* @returns {string}
*/
function stripHighlightMarkers(value) {
return value.split(HIGHLIGHT_START).join("").split(HIGHLIGHT_END).join("").trim();
}
/**
* Formate la date en ignorant l'heure de publication.
* @param {unknown} rawDate Date brute.
@@ -240,58 +347,107 @@ function buildDateInfo(rawDate) {
/**
* Extrait un libelle de section a partir du chemin Hugo.
* @param {string} section Section brute renvoyee par l'index.
* @param {string} path Chemin relatif de l'article.
* @returns {string}
*/
function extractSectionLabel(path) {
const segments = path.split("/").filter(Boolean);
if (segments.length === 0) {
return "Section inconnue";
function extractSectionLabel(section, path) {
let sectionKey = "";
if (typeof section === "string" && section.trim().length > 0) {
sectionKey = section.trim();
} else {
const segments = path.split("/").filter(Boolean);
if (segments.length === 0) {
return "Section inconnue";
}
sectionKey = segments[0];
}
const firstSegment = segments[0];
if (firstSegment === "collections") {
if (sectionKey === "collections") {
return "Collections";
}
if (firstSegment === "critiques") {
if (sectionKey === "critiques") {
return "Critiques";
}
if (firstSegment === "interets") {
if (sectionKey === "interets") {
return "Intérêts";
}
if (firstSegment === "stats") {
if (sectionKey === "stats") {
return "Statistiques";
}
if (firstSegment === "taxonomies") {
if (sectionKey === "taxonomies") {
return "Taxonomies";
}
if (firstSegment === "contact") {
if (sectionKey === "contact") {
return "Contact";
}
if (firstSegment === "manifeste") {
if (sectionKey === "manifeste") {
return "Manifeste";
}
if (firstSegment === "liens-morts") {
if (sectionKey === "liens-morts") {
return "Liens morts";
}
if (firstSegment === "revue-de-presse") {
if (sectionKey === "revue-de-presse") {
return "Revue de presse";
}
return firstSegment;
return sectionKey;
}
/**
* Construit un fragment DOM avec les passages surlignes.
* @param {string} rawText Texte potentiellement marque.
* @returns {DocumentFragment}
*/
function buildHighlightedFragment(rawText) {
const fragment = document.createDocumentFragment();
if (typeof rawText !== "string" || rawText.length === 0) {
return fragment;
}
let remaining = rawText;
while (remaining.length > 0) {
const startIndex = remaining.indexOf(HIGHLIGHT_START);
if (startIndex === -1) {
fragment.append(remaining);
break;
}
if (startIndex > 0) {
fragment.append(remaining.slice(0, startIndex));
}
remaining = remaining.slice(startIndex + HIGHLIGHT_START.length);
const endIndex = remaining.indexOf(HIGHLIGHT_END);
if (endIndex === -1) {
fragment.append(remaining);
break;
}
const mark = document.createElement("mark");
mark.textContent = remaining.slice(0, endIndex);
fragment.append(mark);
remaining = remaining.slice(endIndex + HIGHLIGHT_END.length);
}
return fragment;
}
/**
* Construit un article de resultat simple.
* @param {{title: string, path: string, published_at?: string}} hit Resultat.
* @param {{title: string, titleMarkup: string, summary: string, summaryMarkup: string, path: string, section: string, published_at?: string}} hit Resultat.
* @returns {HTMLElement}
*/
function createResultArticle(hit) {
@@ -300,7 +456,7 @@ function createResultArticle(hit) {
const title = document.createElement("h3");
const link = document.createElement("a");
link.href = hit.path;
link.textContent = hit.title;
link.append(buildHighlightedFragment(hit.titleMarkup));
title.append(link);
article.append(title);
@@ -324,11 +480,18 @@ function createResultArticle(hit) {
meta.append(separator);
const section = document.createElement("span");
section.textContent = extractSectionLabel(hit.path);
section.textContent = extractSectionLabel(hit.section, hit.path);
meta.append(section);
article.append(meta);
if (hit.summary.length > 0) {
const summary = document.createElement("p");
summary.className = "search-result-summary";
summary.append(buildHighlightedFragment(hit.summaryMarkup));
article.append(summary);
}
const path = document.createElement("p");
path.className = "search-result-path";
path.textContent = hit.path;
@@ -340,7 +503,7 @@ function createResultArticle(hit) {
/**
* Rend la liste des resultats.
* @param {HTMLOListElement} listNode Liste cible.
* @param {Array<{title: string, path: string, published_at?: string}>} hits Resultats a afficher.
* @param {Array<{title: string, titleMarkup: string, summary: string, summaryMarkup: string, path: string, section: string, published_at?: string}>} hits Resultats a afficher.
*/
function renderListing(listNode, hits) {
clearNode(listNode);