Seed from NAS prod snapshot 20260130-190531
This commit is contained in:
157
docs/anchors.md
Normal file
157
docs/anchors.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Contrat des ancres (paragraphes opposables)
|
||||
|
||||
## Source de vérité du sélecteur
|
||||
Le site garantit la citabilité des paragraphes via des IDs injectés sur les balises `<p>`.
|
||||
|
||||
**Sélecteur contractuel :**
|
||||
- `.reading p[id^="p-"]`
|
||||
|
||||
Tout outillage (scripts, tests, docs) doit utiliser ce sélecteur comme référence.
|
||||
|
||||
## Ce que le test vérifie
|
||||
Le test compare, page par page, la liste des IDs de paragraphes présents dans `dist/` contre une baseline versionnée.
|
||||
|
||||
- Ajouts d’IDs : généralement OK (nouveaux paragraphes).
|
||||
- Suppressions / churn élevé : alerte (risque de casser des citations existantes).
|
||||
|
||||
## Fichier baseline
|
||||
- `tests/anchors-baseline.json`
|
||||
|
||||
## Commandes
|
||||
1) Générer / mettre à jour la baseline (cas intentionnel) :
|
||||
- `npm run build`
|
||||
- `npm run test:anchors:update`
|
||||
|
||||
2) Vérifier sans changer la baseline (cas normal) :
|
||||
- `npm run build`
|
||||
- `npm run test:anchors`
|
||||
|
||||
## Politique d’échec (pragmatique)
|
||||
Le test échoue si le churn d’une page dépasse un seuil (défaut : 20%) sur une page “suffisamment grande”.
|
||||
|
||||
## Aliases build-time
|
||||
- `src/anchors/anchor-aliases.json`
|
||||
- `scripts/inject-anchor-aliases.mjs`
|
||||
- `scripts/check-anchor-aliases.mjs`
|
||||
- et rappelle : *alias = compat rétro de liens historiques sans JS*
|
||||
|
||||
## Ancres de section (H2) quand on utilise `<details>`
|
||||
Quand un chapitre est structuré en sections repliables (`<details>`), on utilise une ancre “technique” dédiée pour garantir :
|
||||
- une cible stable même si le H2 est dans un bloc replié,
|
||||
- un scroll-offset correct (header + bandeau),
|
||||
- une détection fiable pour le bandeau “reading-follow”.
|
||||
|
||||
Contrat côté HTML rendu :
|
||||
- présence d’un élément de type :
|
||||
- `<span class="details-anchor" id="..."></span>`
|
||||
- puis, dans le même `<details>`, un H2 visible dans le body.
|
||||
|
||||
Contrat côté CSS :
|
||||
- `.details-anchor` a `scroll-margin-top: var(--sticky-offset)`.
|
||||
|
||||
Contrat côté JS (EditionLayout) :
|
||||
- `openDetailsIfNeeded(el)` ouvre le `<details>` parent si nécessaire avant de scroller.
|
||||
|
||||
## Compat “legacy hash” : `#p-<idx>-<hash>`
|
||||
Un hotfix de compat existe pour les anciennes ancres de paragraphes au format :
|
||||
- `#p-<index>-<8 hex>`
|
||||
|
||||
Si l’ID exact n’existe plus :
|
||||
- on cherche le premier élément dont l’id commence par `p-<index>-`
|
||||
- puis scroll avec offset.
|
||||
|
||||
But : éviter les “liens morts” historiques quand une régénération d’IDs a eu lieu.
|
||||
|
||||
Limite : c’est un fallback de dernier recours (moins déterministe qu’un alias explicite).
|
||||
Le mécanisme recommandé reste : `docs/anchor-aliases.json` + injection au build.
|
||||
|
||||
_______________________________________
|
||||
|
||||
Dernière mise à jour : 2026-01-29
|
||||
|
||||
Ce document formalise comment on évite de casser les liens profonds (URLs avec `#ancre`).
|
||||
|
||||
---
|
||||
|
||||
## 1) Principe
|
||||
|
||||
Les IDs d’ancres générés (ou dérivés) peuvent changer :
|
||||
- réécriture
|
||||
- insertion/suppression de paragraphes
|
||||
- re-slug d’un titre
|
||||
|
||||
➡️ Donc : on ne “devine” pas un fallback par index en runtime.
|
||||
➡️ On fait un aliasing **déterministe à la build**, versionné.
|
||||
|
||||
---
|
||||
|
||||
## 2) Le mapping d’alias
|
||||
|
||||
- Fichier versionné (ex) : `docs/anchor-aliases.json`
|
||||
- Format : `oldId -> newId` par page
|
||||
|
||||
Ex en json :
|
||||
{
|
||||
"/archicratie/archicrat-ia/chapitre-4/": {
|
||||
"p-8-ancien": "p-8-nouveau"
|
||||
}
|
||||
}
|
||||
|
||||
## 3) Injection à la build (dans dist)
|
||||
|
||||
Script : scripts/inject-anchor-aliases.mjs
|
||||
|
||||
Moment : postbuild (après astro build, avant pagefind)
|
||||
|
||||
Ce script injecte dans dist/**/index.html un <span id="oldId"></span> juste avant l’élément portant id="newId".
|
||||
|
||||
## 4) Contrôles (incassables)
|
||||
### A) Vérifier qu’on n’a pas d’IDs dupliqués en HTML
|
||||
|
||||
Script : scripts/audit-dist.mjs
|
||||
|
||||
Robustesse :
|
||||
|
||||
ignore <script> et <style> (évite faux positifs)
|
||||
|
||||
exige un espace avant id= (évite data-id=)
|
||||
|
||||
Exécution :
|
||||
npm run audit:dist
|
||||
|
||||
### B) Vérifier que les aliases sont vraiment présents dans dist
|
||||
|
||||
Script : scripts/verify-anchor-aliases-in-dist.mjs
|
||||
|
||||
Exécuté dans npm test
|
||||
|
||||
## 5) Mise à jour assistée (si besoin)
|
||||
|
||||
Pour générer/mettre à jour des aliases à partir des deltas d’ancres :
|
||||
npm run test:anchors:update
|
||||
|
||||
## 6) ⚠️ Artefacts macOS (PaxHeader / ._ / DS_Store)
|
||||
|
||||
Quand on transfère une archive depuis macOS, on peut importer des dossiers/fichiers parasites :
|
||||
|
||||
PaxHeader/
|
||||
|
||||
._*
|
||||
|
||||
.DS_Store
|
||||
|
||||
Ces artefacts peuvent polluer le build (Astro croit voir un “vrai” contenu MDX).
|
||||
|
||||
➡️ Recommandation : .dockerignore strict :
|
||||
# macOS / archives
|
||||
.DS_Store
|
||||
._*
|
||||
**/._*
|
||||
PaxHeader
|
||||
**/PaxHeader
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
dist
|
||||
|
||||
# (Optionnel) Avant build sur NAS : supprimer ces dossiers si présents.
|
||||
Reference in New Issue
Block a user