feat(glossary): add step 21 smart navigation
All checks were successful
SMOKE / smoke (push) Successful in 13s
CI / build-and-anchors (push) Successful in 1m4s
CI / build-and-anchors (pull_request) Successful in 35s

This commit is contained in:
2026-04-26 13:03:45 +02:00
parent 689619d14d
commit 9f88112aca
45 changed files with 1020 additions and 112 deletions

View File

@@ -0,0 +1,99 @@
export type GlossaryDefaultNavigation = {
understand?: string[];
deepen?: string[];
compare?: string[];
apply?: string[];
};
export const GLOSSARY_NAV_DEFAULTS: Record<string, GlossaryDefaultNavigation> = {
"concept-fondamental": {
understand: ["archicratie", "arcalite", "cratialite", "archicration", "co-viabilite", "tension"],
deepen: ["scene-depreuve", "regime-de-co-viabilite", "meta-regime-archicratique"],
compare: ["autarchicratie", "domination-legale-rationnelle", "decisionnisme-souverain"],
apply: ["audit-archicratique", "cartographie-des-scenes-manquantes", "journal-de-justification"],
},
scene: {
understand: ["scene-depreuve", "scene-darchicration", "archicration"],
deepen: ["scene-manquante", "scene-empechee", "institution-invisible", "monde-instituable"],
compare: ["autarchicratie", "obliteration-archicratique", "hypotopie", "atopie"],
apply: ["cartographie-des-scenes-manquantes", "budget-scenique", "tribunal-de-lalgorithme"],
},
dynamique: {
understand: ["archicratisation", "desarchicration", "co-viabilisation"],
deepen: ["archeogenese", "obliteration-archicratique", "regime-de-co-viabilite"],
compare: ["desarchicratisation", "autarchicratie", "archicration-obliteree"],
apply: ["archidiagnostic", "audit-archicratique", "cartographie-des-scenes-manquantes"],
},
pathologie: {
understand: ["autarchicratie", "autarchicration", "archicration-obliteree"],
deepen: ["hyperarcalite", "hypercratialite", "obliteration-archicratique", "desarchicration"],
compare: ["archicration", "scene-depreuve", "monde-instituable", "co-viabilite"],
apply: ["archidiagnostic", "cartographie-des-scenes-manquantes", "coupe-circuit-citoyen"],
},
topologie: {
understand: ["meta-regime", "regime-de-co-viabilite", "synchrotopie"],
deepen: ["hypotopie", "hypertopie", "atopie", "archicrations-differentielles-et-formes-hybrides"],
compare: ["scene-depreuve", "institution-invisible", "monde-instituable"],
apply: ["cartographie-des-scenes-manquantes", "archidiagnostic", "audit-archicratique"],
},
"meta-regime": {
understand: ["meta-regime-archicratique", "archeogenese", "regime-de-co-viabilite"],
deepen: ["archicrations-proto-symboliques", "archicrations-scripturo-normatives", "archicrations-techno-logistiques"],
compare: ["archicrations-marchandes", "archicrations-guerrieres", "archicrations-epistemiques"],
apply: ["archicrations-differentielles-et-formes-hybrides", "archidiagnostic"],
},
paradigme: {
understand: ["archicratie", "co-viabilite", "tension"],
deepen: ["configuration-et-interdependance", "pensee-complexe", "theorie-de-la-justification"],
compare: ["decisionnisme-souverain", "domination-legale-rationnelle", "agencement-machinique", "democratie-deliberative"],
apply: ["audit-archicratique", "cartographie-des-scenes-manquantes"],
},
doctrine: {
understand: ["decisionnisme-souverain", "contractualisme-hobbesien", "volonte-generale"],
deepen: ["exception-souveraine", "droit-naturel-et-propriete", "domination-legale-rationnelle"],
compare: ["democratie-deliberative", "dissensus-politique", "gouvernance-des-communs", "lieu-vide-du-pouvoir"],
apply: ["scene-depreuve", "archicration", "audit-archicratique"],
},
"dispositif-ia": {
understand: ["audit-archicratique", "cartographie-des-scenes-manquantes", "journal-de-justification"],
deepen: ["tribunal-de-lalgorithme", "visa-daffectation", "budget-scenique"],
compare: ["gouvernementalite-algorithmique", "preemption-algorithmique", "institution-invisible"],
apply: ["droit-au-differe-contradictoire", "coupe-circuit-citoyen", "tribunal-de-lalgorithme"],
},
"tension-irreductible": {
understand: ["tension", "co-viabilite", "archicration"],
deepen: ["coexistence-ontologique-et-necessite-regulatrice", "formes-de-vie-et-cadres-dhabitabilite", "souverainetes-territoriales-et-interdependances-globales"],
compare: ["regulation-technique-et-legitimation-democratique", "memoire-symbolique-et-instantaneite-computationnelle", "travail-vivant-et-abstraction-de-la-valeur"],
apply: ["scene-depreuve", "audit-archicratique", "cartographie-des-scenes-manquantes"],
},
figure: {
understand: ["figures-archicratiques", "archicrate", "institution-invisible"],
deepen: ["hyperarcalite", "hypercratialite", "autarchicration"],
compare: ["scene-darchicration", "monde-instituable", "autarchicratie"],
apply: ["archidiagnostic", "cartographie-des-scenes-manquantes"],
},
qualification: {
understand: ["archicratique", "desarchicratique", "archicration"],
deepen: ["archicratisation", "desarchicratisation", "scene-darchicration"],
compare: ["autarchicratie", "archicration-obliteree", "monde-instituable"],
apply: ["archidiagnostic", "audit-archicratique"],
},
epistemologie: {
understand: ["archicratistique", "archidiagnostic", "pensee-complexe"],
deepen: ["archeogenese", "meta-regime-archicratique", "figures-archicratiques"],
compare: ["theorie-de-la-justification", "configuration-et-interdependance", "transduction-et-individuation"],
apply: ["audit-archicratique", "cartographie-des-scenes-manquantes", "journal-de-justification"],
},
};

View File

@@ -1,4 +1,5 @@
import type { CollectionEntry } from "astro:content";
import { GLOSSARY_NAV_DEFAULTS } from "./glossary-navigation-defaults";
export type GlossaryEntry = CollectionEntry<"glossaire">;
@@ -16,6 +17,32 @@ export type GlossaryRelationBlock = GlossaryRelationSection & {
className: string;
};
export type GlossarySmartNavigationPathKey =
| "understand"
| "deepen"
| "compare"
| "apply";
export type GlossarySmartNavigationPath = {
key: GlossarySmartNavigationPathKey;
label: string;
entries: GlossaryEntry[];
};
export type GlossarySmartNavigationFlow = {
key: string;
label: string;
primaryNext?: GlossaryEntry;
primaryReason?: string;
};
export type GlossarySmartNavigation = {
primaryNext?: GlossaryEntry;
primaryReason?: string;
paths: GlossarySmartNavigationPath[];
flows: GlossarySmartNavigationFlow[];
};
export type GlossaryHomeStats = {
totalEntries: number;
paradigmesCount: number;
@@ -122,6 +149,16 @@ export const FAMILY_SECTION_TITLES: Record<string, string> = {
epistemologie: "Outillage épistémologique",
};
export const SMART_NAV_PATH_LABELS: Record<
GlossarySmartNavigationPathKey,
string
> = {
understand: "Comprendre",
deepen: "Approfondir",
compare: "Comparer",
apply: "Appliquer",
};
const PREFERRED_PARADIGME_SLUGS = [
"gouvernementalite",
"gouvernementalite-algorithmique",
@@ -202,35 +239,35 @@ export function uniqueGlossaryEntries(
}
export function slugsOfGlossaryEntries(
entries: GlossaryEntry[] = [],
): Set<string> {
const slugs = new Set<string>();
for (const entry of entries) {
const slug = slugOfGlossaryEntry(entry);
if (!slug) continue;
slugs.add(slug);
}
return slugs;
}
export function excludeGlossaryEntries(
entries: GlossaryEntry[] = [],
excluded: Iterable<string> = [],
): GlossaryEntry[] {
const excludedSlugs = new Set(
Array.from(excluded)
.map((value) => normalizeGlossarySlug(value))
.filter(Boolean),
);
return entries.filter((entry) => {
const slug = slugOfGlossaryEntry(entry);
return Boolean(slug) && !excludedSlugs.has(slug);
});
entries: GlossaryEntry[] = [],
): Set<string> {
const slugs = new Set<string>();
for (const entry of entries) {
const slug = slugOfGlossaryEntry(entry);
if (!slug) continue;
slugs.add(slug);
}
return slugs;
}
export function excludeGlossaryEntries(
entries: GlossaryEntry[] = [],
excluded: Iterable<string> = [],
): GlossaryEntry[] {
const excludedSlugs = new Set(
Array.from(excluded)
.map((value) => normalizeGlossarySlug(value))
.filter(Boolean),
);
return entries.filter((entry) => {
const slug = slugOfGlossaryEntry(entry);
return Boolean(slug) && !excludedSlugs.has(slug);
});
}
export function resolveGlossaryEntriesInSourceOrder(
slugs: string[] = [],
allEntries: GlossaryEntry[] = [],
@@ -467,6 +504,96 @@ export function getRelationBlocks(
].filter((block) => block.items.length > 0);
}
export function getGlossarySmartNavigation(
currentEntry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],
): GlossarySmartNavigation {
const currentSlug = slugOfGlossaryEntry(currentEntry);
const rawNavigation = currentEntry.data.navigation;
const defaultNavigation = GLOSSARY_NAV_DEFAULTS[familyOf(currentEntry)];
const navigationSource = rawNavigation ?? {
primaryNext: undefined,
primaryReason: undefined,
paths: defaultNavigation ?? {},
flows: {},
};
const primaryNext = resolveGlossaryEntriesInSourceOrder(
navigationSource.primaryNext ? [navigationSource.primaryNext] : [],
allEntries,
).find((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
const pathKeys: GlossarySmartNavigationPathKey[] = [
"understand",
"deepen",
"compare",
"apply",
];
const paths = pathKeys
.map((key) => {
const entries = resolveGlossaryEntriesInSourceOrder(
navigationSource.paths?.[key] ?? [],
allEntries,
).filter((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
return {
key,
label: SMART_NAV_PATH_LABELS[key],
entries,
};
})
.filter((path) => path.entries.length > 0);
const flows = Object.entries(navigationSource.flows ?? {})
.map(([key, flow]) => {
const primaryNext = resolveGlossaryEntriesInSourceOrder(
flow.primaryNext ? [flow.primaryNext] : [],
allEntries,
).find((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
return {
key,
label: flow.label,
primaryNext,
primaryReason: flow.primaryReason,
};
})
.filter((flow) => flow.primaryNext);
if (primaryNext || paths.length > 0 || flows.length > 0) {
return {
primaryNext,
primaryReason: navigationSource.primaryReason,
paths,
flows,
};
}
const fallbackEntries = uniqueGlossaryEntries([
...resolveGlossaryEntriesInSourceOrder(
currentEntry.data.related ?? [],
allEntries,
),
...resolveGlossaryEntriesInSourceOrder(
currentEntry.data.seeAlso ?? [],
allEntries,
),
]).filter((entry) => slugOfGlossaryEntry(entry) !== currentSlug);
const fallbackPrimary = fallbackEntries[0];
return {
primaryNext: fallbackPrimary,
primaryReason: fallbackPrimary
? "Ce lien prolonge directement les relations conceptuelles de cette fiche."
: undefined,
paths: [],
flows: [],
};
}
export function getRelationSections(
entry: GlossaryEntry,
allEntries: GlossaryEntry[] = [],