feat(glossaire): enrich entries and refine glossary navigation
All checks were successful
SMOKE / smoke (push) Successful in 9s
CI / build-and-anchors (push) Successful in 55s
CI / build-and-anchors (pull_request) Successful in 45s

This commit is contained in:
2026-03-19 21:53:33 +01:00
parent bfa44fecda
commit d6b4eb82f4
57 changed files with 1471 additions and 429 deletions

View File

@@ -26,7 +26,7 @@ const fondamentaux = fondamentauxWanted
function resolveList(slugs = []) {
return slugs
.map((slug) => bySlug.get(slug))
.map((slug) => bySlug.get(String(slug || "").trim()))
.filter(Boolean);
}
@@ -42,43 +42,138 @@ function uniqueBySlug(entries) {
return out;
}
const relatedEntries = uniqueBySlug(resolveList(currentEntry.data.related ?? []))
.sort((a, b) => collator.compare(a.data.term, b.data.term));
function sortByTerm(entries = []) {
return [...entries].sort((a, b) => collator.compare(a.data.term, b.data.term));
}
const opposedEntries = uniqueBySlug(resolveList(currentEntry.data.opposedTo ?? []))
.sort((a, b) => collator.compare(a.data.term, b.data.term));
function familyOf(entry) {
return entry?.data?.family ?? "";
}
const seeAlsoEntries = uniqueBySlug(resolveList(currentEntry.data.seeAlso ?? []))
.sort((a, b) => collator.compare(a.data.term, b.data.term));
function kindOf(entry) {
return entry?.data?.kind ?? "";
}
const paradigmes = [...allEntries]
.filter((e) => e.data.kind === "paradigme" && slugOf(e) !== currentSlug)
.sort((a, b) => collator.compare(a.data.term, b.data.term));
const relatedEntries = sortByTerm(
uniqueBySlug(resolveList(currentEntry.data.related ?? []))
);
function contextualParadigmsFor(entry) {
const relatedParadigms = (entry.data.related ?? [])
.map((slug) => bySlug.get(slug))
.filter((e) => e && e.data.kind === "paradigme");
const opposedEntries = sortByTerm(
uniqueBySlug(resolveList(currentEntry.data.opposedTo ?? []))
);
const seeAlsoParadigms = (entry.data.seeAlso ?? [])
.map((slug) => bySlug.get(slug))
.filter((e) => e && e.data.kind === "paradigme");
const seeAlsoEntries = sortByTerm(
uniqueBySlug(resolveList(currentEntry.data.seeAlso ?? []))
);
const opposedParadigms = (entry.data.opposedTo ?? [])
.map((slug) => bySlug.get(slug))
.filter((e) => e && e.data.kind === "paradigme");
const familyLabels = {
"concept-fondamental": "Concept fondamental",
scene: "Scène",
dynamique: "Dynamique",
pathologie: "Pathologie",
topologie: "Topologie",
"meta-regime": "Méta-régime",
paradigme: "Paradigme",
doctrine: "Doctrine",
verbe: "Verbe",
"dispositif-ia": "Dispositif IA",
"tension-irreductible": "Tension irréductible",
};
const merged = uniqueBySlug([
...relatedParadigms,
...seeAlsoParadigms,
...opposedParadigms,
]);
const kindLabels = {
concept: "Concept",
diagnostic: "Diagnostic",
topologie: "Topologie",
verbe: "Verbe",
paradigme: "Paradigme",
doctrine: "Doctrine",
};
if (merged.length > 0) {
return merged.slice(0, 5);
const domainLabels = {
transversal: "Transversal",
theorie: "Théorie",
"cas-ia": "Cas IA",
};
const levelLabels = {
fondamental: "Fondamental",
intermediaire: "Intermédiaire",
avance: "Avancé",
};
const currentFamily = familyOf(currentEntry);
const displayFamily =
familyLabels[currentFamily] ??
kindLabels[currentEntry.data.kind] ??
"Fiche";
const displayDomain =
domainLabels[currentEntry.data.domain] ??
currentEntry.data.domain;
const displayLevel =
levelLabels[currentEntry.data.level] ??
currentEntry.data.level;
function entriesOfSameFamily(entry) {
const family = familyOf(entry);
if (!family) return [];
if (family === "concept-fondamental") {
return fondamentaux;
}
if (entry.data.kind === "paradigme") {
return sortByTerm(
allEntries.filter((item) => familyOf(item) === family)
);
}
const sameFamilyEntries = entriesOfSameFamily(currentEntry);
const familySectionTitles = {
"concept-fondamental": "Noyau archicratique",
scene: "Scènes archicratiques",
dynamique: "Dynamiques archicratiques",
pathologie: "Pathologies archicratiques",
topologie: "Topologies voisines",
"meta-regime": "Méta-régimes archicratiques",
paradigme: "Paradigmes voisins",
doctrine: "Doctrines fondatrices",
verbe: "Verbes de la scène",
"dispositif-ia": "Dispositifs IA",
"tension-irreductible": "Tensions irréductibles",
};
const sameFamilyTitle =
familySectionTitles[currentFamily] ?? "Même famille";
function isTheoryEntry(entry) {
const family = familyOf(entry);
const kind = kindOf(entry);
return (
family === "paradigme" ||
family === "doctrine" ||
kind === "paradigme" ||
kind === "doctrine"
);
}
function contextualTheoryFor(entry) {
const fromRelations = uniqueBySlug([
...resolveList(entry.data.related ?? []),
...resolveList(entry.data.seeAlso ?? []),
...resolveList(entry.data.opposedTo ?? []),
])
.filter((item) => slugOf(item) !== currentSlug)
.filter((item) => isTheoryEntry(item));
if (fromRelations.length > 0) {
return sortByTerm(fromRelations).slice(0, 6);
}
if (familyOf(entry) === "paradigme") {
const preferred = [
"gouvernementalite",
"gouvernementalite-algorithmique",
@@ -100,6 +195,22 @@ function contextualParadigmsFor(entry) {
"grammatisation-et-proletarisation-cognitive",
];
return uniqueBySlug(
preferred
.filter((slug) => slug !== currentSlug)
.map((slug) => bySlug.get(slug))
.filter(Boolean)
).slice(0, 8);
}
if (familyOf(entry) === "doctrine") {
const preferred = [
"contractualisme-hobbesien",
"droit-naturel-et-propriete",
"volonte-generale",
"decisionnisme-souverain",
];
return uniqueBySlug(
preferred
.filter((slug) => slug !== currentSlug)
@@ -108,55 +219,56 @@ function contextualParadigmsFor(entry) {
).slice(0, 6);
}
return paradigmes.slice(0, 5);
return [];
}
const contextualParadigms = contextualParadigmsFor(currentEntry);
const contextualTheory = contextualTheoryFor(currentEntry);
const kindLabels = {
concept: "Concept",
diagnostic: "Diagnostic",
topologie: "Topologie",
verbe: "Verbe",
paradigme: "Paradigme",
doctrine: "Doctrine",
};
const showNoyau = currentFamily !== "concept-fondamental" && fondamentaux.length > 0;
const domainLabels = {
transversal: "Transversal",
theorie: "Théorie",
"cas-ia": "Cas IA",
};
const showSameFamily =
sameFamilyEntries.length > 0 && currentFamily !== "concept-fondamental";
const levelLabels = {
fondamental: "Fondamental",
intermediaire: "Intermédiaire",
avance: "Avancé",
};
const metaLabel = [
kindLabels[currentEntry.data.kind] ?? currentEntry.data.kind,
domainLabels[currentEntry.data.domain] ?? currentEntry.data.domain,
levelLabels[currentEntry.data.level] ?? currentEntry.data.level,
].join(" · ");
const relationSections = [
{
title: "Concepts liés",
items: relatedEntries,
},
{
title: "En tension avec",
items: opposedEntries,
},
{
title: "Voir aussi",
items: seeAlsoEntries,
},
].filter((section) => section.items.length > 0);
---
<nav class="glossary-aside" aria-label="Navigation du glossaire">
<div class="glossary-aside__block glossary-aside__block--intro">
<a class="glossary-aside__back" href="/glossaire/">← Retour au glossaire</a>
<div class="glossary-aside__title">Glossaire archicratique</div>
<div class="glossary-aside__meta">{metaLabel}</div>
<div class="glossary-aside__pills" aria-label="Repères de lecture">
<span class="glossary-aside__pill glossary-aside__pill--family">
{displayFamily}
</span>
<span class="glossary-aside__pill">{displayDomain}</span>
<span class="glossary-aside__pill">{displayLevel}</span>
</div>
</div>
<section class="glossary-aside__block">
<h2 class="glossary-aside__heading">Portails</h2>
<ul class="glossary-aside__list">
<li><a href="/glossaire/">Index général</a></li>
<li><a href="/glossaire/paradigmes/">Page paradigmes</a></li>
<li><a href="/glossaire/archicrations/">Archicrations</a></li>
<li><a href="/glossaire/paradigmes/">Paradigmes et doctrines</a></li>
</ul>
</section>
{fondamentaux.length > 0 && (
{showNoyau && (
<section class="glossary-aside__block">
<h2 class="glossary-aside__heading">Noyau archicratique</h2>
<ul class="glossary-aside__list">
@@ -178,52 +290,50 @@ const metaLabel = [
</section>
)}
{(relatedEntries.length > 0 || opposedEntries.length > 0 || seeAlsoEntries.length > 0) && (
{showSameFamily && (
<section class="glossary-aside__block">
<h2 class="glossary-aside__heading">Autour de cette fiche</h2>
{relatedEntries.length > 0 && (
<>
<h3 class="glossary-aside__subheading">Liés</h3>
<ul class="glossary-aside__list">
{relatedEntries.map((entry) => (
<li><a href={hrefOf(entry)}>{entry.data.term}</a></li>
))}
</ul>
</>
)}
{opposedEntries.length > 0 && (
<>
<h3 class="glossary-aside__subheading">Opposés</h3>
<ul class="glossary-aside__list">
{opposedEntries.map((entry) => (
<li><a href={hrefOf(entry)}>{entry.data.term}</a></li>
))}
</ul>
</>
)}
{seeAlsoEntries.length > 0 && (
<>
<h3 class="glossary-aside__subheading">Voir aussi</h3>
<ul class="glossary-aside__list">
{seeAlsoEntries.map((entry) => (
<li><a href={hrefOf(entry)}>{entry.data.term}</a></li>
))}
</ul>
</>
)}
<h2 class="glossary-aside__heading">{sameFamilyTitle}</h2>
<ul class="glossary-aside__list">
{sameFamilyEntries.map((entry) => {
const active = slugOf(entry) === currentSlug;
return (
<li>
<a
href={hrefOf(entry)}
aria-current={active ? "page" : undefined}
class={active ? "is-active" : undefined}
>
{entry.data.term}
</a>
</li>
);
})}
</ul>
</section>
)}
{contextualParadigms.length > 0 && (
{relationSections.length > 0 && (
<section class="glossary-aside__block">
<h2 class="glossary-aside__heading">
{currentEntry.data.kind === "paradigme" ? "Paradigmes voisins" : "Paradigmes mobilisés"}
</h2>
<h2 class="glossary-aside__heading">Autour de cette fiche</h2>
{relationSections.map((section) => (
<>
<h3 class="glossary-aside__subheading">{section.title}</h3>
<ul class="glossary-aside__list">
{section.items.map((entry) => (
<li><a href={hrefOf(entry)}>{entry.data.term}</a></li>
))}
</ul>
</>
))}
</section>
)}
{contextualTheory.length > 0 && (
<section class="glossary-aside__block">
<h2 class="glossary-aside__heading">Paysage théorique</h2>
<ul class="glossary-aside__list">
{contextualParadigms.map((entry) => (
{contextualTheory.map((entry) => (
<li><a href={hrefOf(entry)}>{entry.data.term}</a></li>
))}
</ul>
@@ -265,11 +375,28 @@ const metaLabel = [
line-height: 1.25;
}
.glossary-aside__meta{
.glossary-aside__pills{
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.glossary-aside__pill{
display: inline-flex;
align-items: center;
padding: 4px 9px;
border: 1px solid rgba(127,127,127,0.24);
border-radius: 999px;
background: rgba(127,127,127,0.04);
font-size: 12px;
line-height: 1.35;
opacity: .78;
line-height: 1.3;
opacity: .9;
}
.glossary-aside__pill--family{
border-color: rgba(127,127,127,0.38);
font-weight: 800;
}
.glossary-aside__heading{
@@ -309,7 +436,8 @@ const metaLabel = [
}
@media (prefers-color-scheme: dark){
.glossary-aside__block{
.glossary-aside__block,
.glossary-aside__pill{
background: rgba(255,255,255,0.04);
}
}