Compare commits
31 Commits
chore/fix-
...
fix/annota
| Author | SHA1 | Date | |
|---|---|---|---|
| 4dfd3b026b | |||
| c93f274f41 | |||
| dfa311fb5b | |||
| 3ef1dc2801 | |||
| 435e41ed4d | |||
| 8825932159 | |||
| b55decbea4 | |||
| 414a848db3 | |||
| cbd4f3a57f | |||
| 49f8d6a95e | |||
| 5afa5cbfda | |||
| a1b1df38ba | |||
| d3f7d74da7 | |||
| 6919190107 | |||
| 021ef5abd7 | |||
| 76cdc85f9c | |||
| f2f6df2127 | |||
| dfe13757f7 | |||
| 148ac997df | |||
| 84492d2741 | |||
| 81baadd57f | |||
| 63d0ffc5fc | |||
| 24143fc2c4 | |||
| 55370b704f | |||
| b8a3ce1337 | |||
| 7f9baedf41 | |||
| 1adbe1c7a3 | |||
| 107a26352f | |||
| 1c2b9ddbb6 | |||
| be99460d4d | |||
| 941fbf5845 |
@@ -159,16 +159,32 @@ jobs:
|
||||
HAS_FULL=0
|
||||
HAS_HOTPATCH=0
|
||||
|
||||
# FULL si build-impacting (ce que tu veux : content/anchors/pages/scripts)
|
||||
if grep -qE '^(src/content/|src/anchors/|src/pages/|scripts/)' /tmp/changed.txt; then
|
||||
HAS_FULL=1
|
||||
fi
|
||||
|
||||
# HOTPATCH si annotations/media touchés
|
||||
if grep -qE '^(src/annotations/|public/media/)' /tmp/changed.txt; then
|
||||
HAS_HOTPATCH=1
|
||||
fi
|
||||
|
||||
# FULL si build-impacting (robuste)
|
||||
# 1) Tout src/ SAUF src/annotations/
|
||||
if grep -qE '^src/' /tmp/changed.txt && grep -qEv '^src/annotations/' /tmp/changed.txt; then
|
||||
HAS_FULL=1
|
||||
fi
|
||||
|
||||
# 2) scripts/
|
||||
if grep -qE '^scripts/' /tmp/changed.txt; then
|
||||
HAS_FULL=1
|
||||
fi
|
||||
|
||||
# 3) Tout public/ SAUF public/media/
|
||||
if grep -qE '^public/' /tmp/changed.txt && grep -qEv '^public/media/' /tmp/changed.txt; then
|
||||
HAS_FULL=1
|
||||
fi
|
||||
|
||||
# 4) fichiers racine qui changent le build / l’image
|
||||
if grep -qE '^(package\.json|package-lock\.json|astro\.config\.mjs|tsconfig\.json|\.npmrc|\.nvmrc|Dockerfile|docker-compose\.yml|nginx\.conf)$' /tmp/changed.txt; then
|
||||
HAS_FULL=1
|
||||
fi
|
||||
|
||||
echo "Gate flags: HAS_FULL=${HAS_FULL} HAS_HOTPATCH=${HAS_HOTPATCH}"
|
||||
|
||||
# Décision
|
||||
|
||||
@@ -25,6 +25,19 @@ Objectif : déployer une nouvelle version du site sur le NAS (DS220+) sans jamai
|
||||
|
||||
➡️ Déploiement = `docs/DEPLOY_PROD_SYNOLOGY_DS220.md` (procédure détaillée, à jour).
|
||||
|
||||
## Mise à jour (2026-03-03) — Gate CI de déploiement (SKIP / HOTPATCH / FULL) + preuves A/B
|
||||
|
||||
La procédure de déploiement “vivante” est désormais pilotée par **Gitea Actions** via le workflow :
|
||||
- `.gitea/workflows/deploy-staging-live.yml`
|
||||
|
||||
Ce workflow décide automatiquement :
|
||||
- **FULL** (rebuild + restart blue + green) dès qu’un changement impacte le build (ex: `src/content/`, `src/pages/`, `scripts/`, `src/anchors/`, etc.)
|
||||
- **HOTPATCH** (patch JSON + copie media) quand le changement ne concerne que `src/annotations/` et/ou `public/media/`
|
||||
- **SKIP** sinon
|
||||
|
||||
Les preuves et la procédure de test reproductible A/B sont documentées dans :
|
||||
➡️ `docs/runbooks/DEPLOY-BLUE-GREEN.md` → section “CI Deploy gate (merge-proof) + Tests A/B + preuve alias injection”.
|
||||
|
||||
## Schéma (résumé, sans commandes)
|
||||
|
||||
- Ne jamais toucher au slot live.
|
||||
|
||||
@@ -202,4 +202,33 @@ docker compose logs --tail=200 web_blue
|
||||
docker compose logs --tail=200 web_green
|
||||
|
||||
# Si tu veux suivre en live :
|
||||
docker compose logs -f web_green
|
||||
docker compose logs -f web_green
|
||||
|
||||
|
||||
## Historique synthétique (2026-03-03) — Stabilisation CI/CD “zéro surprise”
|
||||
|
||||
### Problème initial observé
|
||||
- Déploiement parfois lancé en “hotpatch” alors qu’un rebuild était nécessaire.
|
||||
- Sur merge commits, la détection de fichiers modifiés pouvait être ambiguë.
|
||||
- Résultat : besoin de `force=1` manuel pour éviter des incohérences.
|
||||
|
||||
### Correctif appliqué
|
||||
- Gate CI rendu **merge-proof** :
|
||||
- lecture de `BEFORE` et `AFTER` depuis `event.json`
|
||||
- calcul des fichiers modifiés via `git diff --name-only BEFORE AFTER`
|
||||
|
||||
- Politique de décision stabilisée :
|
||||
- FULL auto dès qu’un changement impacte build/runtime (content/pages/scripts/anchors/etc.)
|
||||
- HOTPATCH auto uniquement pour annotations/media
|
||||
|
||||
### Preuves
|
||||
- Test A (touch src/content) :
|
||||
- Gate flags: HAS_FULL=1 HAS_HOTPATCH=0 → MODE=full
|
||||
- Test B (touch src/annotations) :
|
||||
- Gate flags: HAS_FULL=0 HAS_HOTPATCH=1 → MODE=hotpatch
|
||||
|
||||
### Audit post-déploiement (preuves côté NAS)
|
||||
- 8081 + 8082 répondent HTTP 200
|
||||
- `/para-index.json` + `/annotations-index.json` OK
|
||||
- Aliases injectés visibles dans HTML via `.para-alias` quand alias présent
|
||||
|
||||
|
||||
@@ -199,4 +199,348 @@ Ne jamais modifier dist/ “à la main” sur NAS.
|
||||
|
||||
Si un hotfix prod est indispensable : documenter et backporter via PR Gitea.
|
||||
|
||||
Le canonical dépend du build : PUBLIC_SITE doit être injecté (voir runbook ENV-PUBLIC_SITE).
|
||||
Le canonical dépend du build : PUBLIC_SITE doit être injecté (voir runbook ENV-PUBLIC_SITE).
|
||||
|
||||
## 10) CI Deploy (Gitea Actions) — Gate SKIP / HOTPATCH / FULL (merge-proof) + preuves
|
||||
|
||||
Cette section documente le comportement **canonique** du workflow :
|
||||
- `.gitea/workflows/deploy-staging-live.yml`
|
||||
|
||||
Objectif : **zéro surprise**.
|
||||
On ne veut plus “penser à force=1”.
|
||||
Le gate doit décider automatiquement, y compris sur des **merge commits**.
|
||||
|
||||
### 10.1 — Principe (ce que fait réellement le gate)
|
||||
|
||||
Le job `deploy` calcule les fichiers modifiés entre :
|
||||
- `BEFORE` = commit précédent (avant le push sur main)
|
||||
- `AFTER` = commit actuel (après le push / merge sur main)
|
||||
|
||||
Puis il classe le déploiement dans un mode :
|
||||
|
||||
- **MODE=full**
|
||||
- rebuild image + restart `archicratie-web-blue` (8081) + `archicratie-web-green` (8082)
|
||||
- warmup endpoints (para-index, annotations-index, pagefind.js)
|
||||
- vérification canonical staging + live
|
||||
|
||||
- **MODE=hotpatch**
|
||||
- rebuild d’un `annotations-index.json` consolidé depuis `src/annotations/**`
|
||||
- patch direct dans les conteneurs en cours d’exécution (blue+green)
|
||||
- copie des médias modifiés `public/media/**` vers `/usr/share/nginx/html/media/**`
|
||||
- smoke sur `/annotations-index.json` des deux ports
|
||||
|
||||
- **MODE=skip**
|
||||
- pas de déploiement (on évite le bruit)
|
||||
|
||||
⚠️ Important : le mode “hotpatch” **ne rebuild pas** Astro.
|
||||
Donc toute modification de contenu, routes, scripts, anchors, etc. doit déclencher **full**.
|
||||
|
||||
### 10.2 — Matrice de décision (règles officielles)
|
||||
|
||||
Le gate définit deux flags :
|
||||
- `HAS_FULL=1` si changement “build-impacting”
|
||||
- `HAS_HOTPATCH=1` si changement “annotations/media only”
|
||||
|
||||
Règle de priorité :
|
||||
1) Si `HAS_FULL=1` → **MODE=full**
|
||||
2) Sinon si `HAS_HOTPATCH=1` → **MODE=hotpatch**
|
||||
3) Sinon → **MODE=skip**
|
||||
|
||||
#### 10.2.1 — Changements qui déclenchent FULL (build-impacting)
|
||||
|
||||
Exemples typiques (non exhaustif, mais on couvre le cœur) :
|
||||
- `src/content/**` (contenu MD/MDX)
|
||||
- `src/pages/**` (routes Astro)
|
||||
- `src/anchors/**` (aliases d’ancres)
|
||||
- `scripts/**` (tooling postbuild : injection, index, tests)
|
||||
- `src/layouts/**`, `src/components/**`, `src/styles/**` (rendu et scripts inline)
|
||||
- `astro.config.mjs`, `package.json`, `package-lock.json`
|
||||
- `Dockerfile`, `docker-compose.yml`, `nginx.conf`
|
||||
- `.gitea/workflows/**` (changement infra CI/CD)
|
||||
|
||||
=> On veut **full** pour garantir cohérence et éviter “site partiellement mis à jour”.
|
||||
|
||||
#### 10.2.2 — Changements qui déclenchent HOTPATCH (sans rebuild)
|
||||
|
||||
Uniquement :
|
||||
- `src/annotations/**` (shards YAML)
|
||||
- `public/media/**` (assets média)
|
||||
|
||||
=> On veut hotpatch pour vitesse et éviter rebuild NAS.
|
||||
|
||||
### 10.3 — “Merge-proof” : pourquoi on ne lit PAS seulement `git show $SHA`
|
||||
|
||||
Sur un merge commit, `git show --name-only $SHA` peut être trompeur selon le contexte.
|
||||
La méthode robuste est :
|
||||
- utiliser `event.json` (Gitea Actions) pour récupérer `before` et `after`
|
||||
- calculer `git diff --name-only BEFORE AFTER`
|
||||
|
||||
C’est ce qui rend le gate **merge-proof**.
|
||||
|
||||
### 10.4 — Tests de preuve A/B (reproductibles)
|
||||
|
||||
Ces tests valident le gate sans ambiguïté.
|
||||
But : vérifier que le mode choisi est EXACTEMENT celui attendu.
|
||||
|
||||
#### Test A — toucher `src/content/...` (FULL auto)
|
||||
|
||||
1) Créer une branche test
|
||||
2) Modifier 1 fichier dans `src/content/` (ex : ajouter une ligne de commentaire non destructive)
|
||||
3) PR → merge dans `main`
|
||||
4) Vérifier dans `deploy-staging-live.yml` :
|
||||
|
||||
Attendus :
|
||||
- `Gate flags: HAS_FULL=1 HAS_HOTPATCH=0`
|
||||
- `✅ build-impacting change -> MODE=full (rebuild+restart)`
|
||||
- Les étapes FULL (blue puis green) s’exécutent réellement
|
||||
|
||||
#### Test B — toucher `src/annotations/...` uniquement (HOTPATCH auto)
|
||||
|
||||
1) Créer une branche test
|
||||
2) Modifier 1 fichier sous `src/annotations/**` (ex: un champ comment, ts, etc.)
|
||||
3) PR → merge dans `main`
|
||||
4) Vérifier dans `deploy-staging-live.yml` :
|
||||
|
||||
Attendus :
|
||||
- `Gate flags: HAS_FULL=0 HAS_HOTPATCH=1`
|
||||
- `✅ annotations/media change -> MODE=hotpatch`
|
||||
- Les étapes FULL sont “skip” (durée 0s)
|
||||
- L’étape HOTPATCH s’exécute réellement
|
||||
|
||||
### 10.5 — Preuve opérationnelle côté NAS (2 URLs + 2 commandes)
|
||||
|
||||
But : prouver que staging+live servent bien les endpoints essentiels (et que le déploiement n’a pas “fait semblant”).
|
||||
|
||||
#### 10.5.1 — Deux URLs à vérifier (staging et live)
|
||||
|
||||
- Staging (blue) : `http://127.0.0.1:8081/`
|
||||
- Live (green) : `http://127.0.0.1:8082/`
|
||||
|
||||
#### 10.5.2 — Deux commandes minimales (zéro débat)
|
||||
|
||||
```bash
|
||||
curl -fsSI http://127.0.0.1:8081/ | head -n 1
|
||||
curl -fsSI http://127.0.0.1:8082/ | head -n 1
|
||||
|
||||
---
|
||||
|
||||
## 10) CI Deploy (Gitea Actions) — Gate SKIP / HOTPATCH / FULL (merge-proof) + preuves
|
||||
|
||||
Cette section documente le comportement **canonique** du workflow :
|
||||
- `.gitea/workflows/deploy-staging-live.yml`
|
||||
|
||||
Objectif : **zéro surprise**.
|
||||
On ne veut plus “penser à force=1”.
|
||||
Le gate doit décider automatiquement, y compris sur des **merge commits**.
|
||||
|
||||
### 10.1 — Principe (ce que fait réellement le gate)
|
||||
|
||||
Le job `deploy` calcule les fichiers modifiés entre :
|
||||
- `BEFORE` = commit précédent (avant le push sur main)
|
||||
- `AFTER` = commit actuel (après le push / merge sur main)
|
||||
|
||||
Puis il classe le déploiement dans un mode :
|
||||
|
||||
- **MODE=full**
|
||||
- rebuild image + restart `archicratie-web-blue` (8081) + `archicratie-web-green` (8082)
|
||||
- warmup endpoints (para-index, annotations-index, pagefind.js)
|
||||
- vérification canonical staging + live
|
||||
|
||||
- **MODE=hotpatch**
|
||||
- rebuild d’un `annotations-index.json` consolidé depuis `src/annotations/**`
|
||||
- patch direct dans les conteneurs en cours d’exécution (blue+green)
|
||||
- copie des médias modifiés `public/media/**` vers `/usr/share/nginx/html/media/**`
|
||||
- smoke sur `/annotations-index.json` des deux ports
|
||||
|
||||
- **MODE=skip**
|
||||
- pas de déploiement (on évite le bruit)
|
||||
|
||||
⚠️ Important : le mode “hotpatch” **ne rebuild pas** Astro.
|
||||
Donc toute modification de contenu, routes, scripts, anchors, etc. doit déclencher **full**.
|
||||
|
||||
### 10.2 — Matrice de décision (règles officielles)
|
||||
|
||||
Le gate définit deux flags :
|
||||
- `HAS_FULL=1` si changement “build-impacting”
|
||||
- `HAS_HOTPATCH=1` si changement “annotations/media only”
|
||||
|
||||
Règle de priorité :
|
||||
1) Si `HAS_FULL=1` → **MODE=full**
|
||||
2) Sinon si `HAS_HOTPATCH=1` → **MODE=hotpatch**
|
||||
3) Sinon → **MODE=skip**
|
||||
|
||||
#### 10.2.1 — Changements qui déclenchent FULL (build-impacting)
|
||||
|
||||
Exemples typiques (non exhaustif, mais on couvre le cœur) :
|
||||
- `src/content/**` (contenu MD/MDX)
|
||||
- `src/pages/**` (routes Astro)
|
||||
- `src/anchors/**` (aliases d’ancres)
|
||||
- `scripts/**` (tooling postbuild : injection, index, tests)
|
||||
- `src/layouts/**`, `src/components/**`, `src/styles/**` (rendu et scripts inline)
|
||||
- `astro.config.mjs`, `package.json`, `package-lock.json`
|
||||
- `Dockerfile`, `docker-compose.yml`, `nginx.conf`
|
||||
- `.gitea/workflows/**` (changement infra CI/CD)
|
||||
|
||||
=> On veut **full** pour garantir cohérence et éviter “site partiellement mis à jour”.
|
||||
|
||||
#### 10.2.2 — Changements qui déclenchent HOTPATCH (sans rebuild)
|
||||
|
||||
Uniquement :
|
||||
- `src/annotations/**` (shards YAML)
|
||||
- `public/media/**` (assets média)
|
||||
|
||||
=> On veut hotpatch pour vitesse et éviter rebuild NAS.
|
||||
|
||||
### 10.3 — “Merge-proof” : pourquoi on ne lit PAS seulement `git show $SHA`
|
||||
|
||||
Sur un merge commit, `git show --name-only $SHA` peut être trompeur selon le contexte.
|
||||
La méthode robuste est :
|
||||
- utiliser `event.json` (Gitea Actions) pour récupérer `before` et `after`
|
||||
- calculer `git diff --name-only BEFORE AFTER`
|
||||
|
||||
C’est ce qui rend le gate **merge-proof**.
|
||||
|
||||
### 10.4 — Tests de preuve A/B (reproductibles)
|
||||
|
||||
Ces tests valident le gate sans ambiguïté.
|
||||
But : vérifier que le mode choisi est EXACTEMENT celui attendu.
|
||||
|
||||
#### Test A — toucher `src/content/...` (FULL auto)
|
||||
|
||||
1) Créer une branche test
|
||||
2) Modifier 1 fichier dans `src/content/` (ex : ajouter une ligne de commentaire non destructive)
|
||||
3) PR → merge dans `main`
|
||||
4) Vérifier dans `deploy-staging-live.yml` :
|
||||
|
||||
Attendus :
|
||||
- `Gate flags: HAS_FULL=1 HAS_HOTPATCH=0`
|
||||
- `✅ build-impacting change -> MODE=full (rebuild+restart)`
|
||||
- Les étapes FULL (blue puis green) s’exécutent réellement
|
||||
|
||||
#### Test B — toucher `src/annotations/...` uniquement (HOTPATCH auto)
|
||||
|
||||
1) Créer une branche test
|
||||
2) Modifier 1 fichier sous `src/annotations/**` (ex: un champ comment, ts, etc.)
|
||||
3) PR → merge dans `main`
|
||||
4) Vérifier dans `deploy-staging-live.yml` :
|
||||
|
||||
Attendus :
|
||||
- `Gate flags: HAS_FULL=0 HAS_HOTPATCH=1`
|
||||
- `✅ annotations/media change -> MODE=hotpatch`
|
||||
- Les étapes FULL sont “skip” (durée 0s)
|
||||
- L’étape HOTPATCH s’exécute réellement
|
||||
|
||||
### 10.5 — Preuve opérationnelle côté NAS (2 URLs + 2 commandes)
|
||||
|
||||
But : prouver que staging+live servent bien les endpoints essentiels (et que le déploiement n’a pas “fait semblant”).
|
||||
|
||||
#### 10.5.1 — Deux URLs à vérifier (staging et live)
|
||||
|
||||
- Staging (blue) : `http://127.0.0.1:8081/`
|
||||
- Live (green) : `http://127.0.0.1:8082/`
|
||||
|
||||
#### 10.5.2 — Deux commandes minimales (zéro débat)
|
||||
|
||||
en bash :
|
||||
curl -fsSI http://127.0.0.1:8081/ | head -n 1
|
||||
curl -fsSI http://127.0.0.1:8082/ | head -n 1
|
||||
|
||||
Attendu : HTTP/1.1 200 OK des deux côtés.
|
||||
|
||||
10.6 — Preuve “alias injection” (ancre ancienne → nouvelle) sur une page
|
||||
|
||||
Contexte : lorsqu’un paragraphe change (ex: ticket “Proposer” appliqué),
|
||||
l’ID de paragraphe peut changer, mais on doit préserver les liens anciens via :
|
||||
|
||||
src/anchors/anchor-aliases.json
|
||||
|
||||
injection build-time dans dist (span .para-alias)
|
||||
|
||||
10.6.1 — Check rapide (staging + live)
|
||||
|
||||
Remplacer OLD/NEW par tes ids réels :
|
||||
|
||||
Attendu : HTTP/1.1 200 OK des deux côtés.
|
||||
|
||||
10.6 — Preuve “alias injection” (ancre ancienne → nouvelle) sur une page
|
||||
|
||||
Contexte : lorsqu’un paragraphe change (ex: ticket “Proposer” appliqué),
|
||||
l’ID de paragraphe peut changer, mais on doit préserver les liens anciens via :
|
||||
|
||||
src/anchors/anchor-aliases.json
|
||||
|
||||
injection build-time dans dist (span .para-alias)
|
||||
|
||||
10.6.1 — Check rapide (staging + live)
|
||||
|
||||
Remplacer OLD/NEW par tes ids réels :
|
||||
|
||||
OLD="p-1-60c7ea48"
|
||||
NEW="p-1-a21087b0"
|
||||
|
||||
for P in 8081 8082; do
|
||||
echo "=== $P ==="
|
||||
HTML="$(curl -fsS "http://127.0.0.1:${P}/archicrat-ia/chapitre-3/" | tr -d '\r')"
|
||||
echo "OLD count: $(printf '%s' "$HTML" | grep -o "$OLD" | wc -l | tr -d ' ')"
|
||||
echo "NEW count: $(printf '%s' "$HTML" | grep -o "$NEW" | wc -l | tr -d ' ')"
|
||||
printf '%s\n' "$HTML" | grep -nE "$OLD|$NEW|class=\"para-alias\"" | head -n 40 || true
|
||||
done
|
||||
|
||||
Attendu :
|
||||
|
||||
présence d’un alias : <span id="$OLD" class="para-alias"...>
|
||||
|
||||
présence du nouveau paragraphe : <p id="$NEW">...
|
||||
|
||||
10.6.2 — Check “lien ancien ne casse pas” (HTTP 200)
|
||||
|
||||
for P in 8081 8082; do
|
||||
curl -fsSI "http://127.0.0.1:${P}/archicrat-ia/chapitre-3/#${OLD}" | head -n 1
|
||||
done
|
||||
|
||||
Attendu : HTTP/1.1 200 OK et navigation fonctionnelle côté navigateur.
|
||||
|
||||
10.7 — Troubleshooting gate (symptômes typiques)
|
||||
Symptom 1 : job bloqué “Set up job” très longtemps
|
||||
|
||||
Causes fréquentes :
|
||||
|
||||
runner indisponible / capacity saturée
|
||||
|
||||
runner ne récupère pas les tâches (fetch_timeout trop court + réseau instable)
|
||||
|
||||
erreur dans “Gate — decide …” qui casse bash (et donne l’impression d’un hang)
|
||||
|
||||
Commandes NAS (diagnostic rapide) :
|
||||
|
||||
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' | grep -E 'gitea-act-runner|registry|archicratie-web'
|
||||
docker logs --since 30m --tail 400 gitea-act-runner | tail -n 200
|
||||
Symptom 2 : conditional binary operator expected
|
||||
|
||||
Cause :
|
||||
|
||||
test bash du type [[ "$X" == "1" && "$Y" == "2" ]] mal formé
|
||||
|
||||
variable vide non quotée
|
||||
|
||||
usage d’un opérateur non supporté dans la shell effective
|
||||
|
||||
Fix :
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
toujours quoter : [[ "${VAR:-}" == "..." ]]
|
||||
|
||||
logguer BEFORE/AFTER/FORCE et s’assurer qu’ils ne sont pas vides
|
||||
|
||||
Symptom 3 : le gate liste “trop de fichiers” alors qu’on a changé 1 seul fichier
|
||||
|
||||
Cause :
|
||||
|
||||
comparaison faite sur le mauvais range (ex: git show sur merge, ou mauvais parent)
|
||||
Fix :
|
||||
|
||||
toujours utiliser git diff --name-only "$BEFORE" "$AFTER" (merge-proof)
|
||||
|
||||
confirmer dans le log : Gate ctx: BEFORE=... AFTER=...
|
||||
|
||||
|
||||
226
package-lock.json
generated
226
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.3.13",
|
||||
"astro": "^5.17.3"
|
||||
"astro": "^5.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/sitemap": "^3.7.0",
|
||||
@@ -1247,9 +1247,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
|
||||
"integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1260,9 +1260,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1273,9 +1273,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1286,9 +1286,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz",
|
||||
"integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1299,9 +1299,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1312,9 +1312,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz",
|
||||
"integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1325,9 +1325,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz",
|
||||
"integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1338,9 +1338,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz",
|
||||
"integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1351,9 +1351,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1364,9 +1364,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1377,9 +1377,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -1390,9 +1390,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -1403,9 +1403,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1416,9 +1416,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1429,9 +1429,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1442,9 +1442,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1455,9 +1455,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -1468,9 +1468,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1481,9 +1481,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1494,9 +1494,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz",
|
||||
"integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1507,9 +1507,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1520,9 +1520,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz",
|
||||
"integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1533,9 +1533,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz",
|
||||
"integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1546,9 +1546,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1559,9 +1559,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
|
||||
"integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1905,9 +1905,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "5.17.3",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.17.3.tgz",
|
||||
"integrity": "sha512-69dcfPe8LsHzklwj+hl+vunWUbpMB6pmg35mACjetxbJeUNNys90JaBM8ZiwsPK689SAj/4Zqb1ayaANls9/MA==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.18.0.tgz",
|
||||
"integrity": "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.13.0",
|
||||
@@ -2883,9 +2883,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/devalue": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz",
|
||||
"integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==",
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz",
|
||||
"integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/devlop": {
|
||||
@@ -5690,9 +5690,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
|
||||
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
|
||||
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
@@ -5705,31 +5705,31 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.55.1",
|
||||
"@rollup/rollup-android-arm64": "4.55.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.55.1",
|
||||
"@rollup/rollup-darwin-x64": "4.55.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.55.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.55.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.55.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.55.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.55.1",
|
||||
"@rollup/rollup-openbsd-x64": "4.55.1",
|
||||
"@rollup/rollup-openharmony-arm64": "4.55.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.55.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.55.1",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.55.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.55.1",
|
||||
"@rollup/rollup-android-arm-eabi": "4.59.0",
|
||||
"@rollup/rollup-android-arm64": "4.59.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.59.0",
|
||||
"@rollup/rollup-darwin-x64": "4.59.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.59.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.59.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.59.0",
|
||||
"@rollup/rollup-openbsd-x64": "4.59.0",
|
||||
"@rollup/rollup-openharmony-arm64": "4.59.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.59.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.59.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -6138,9 +6138,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/underscore": {
|
||||
"version": "1.13.7",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz",
|
||||
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==",
|
||||
"version": "1.13.8",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz",
|
||||
"integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.3.13",
|
||||
"astro": "^5.17.3"
|
||||
"astro": "^5.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/sitemap": "^3.7.0",
|
||||
|
||||
0
public/media/.gitkeep
Normal file
0
public/media/.gitkeep
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 816 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 822 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 822 KiB |
@@ -1 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone","orientation":"any"}
|
||||
0
src/annotations/.gitkeep
Normal file
0
src/annotations/.gitkeep
Normal file
@@ -1,10 +0,0 @@
|
||||
schema: 1
|
||||
page: archicrat-ia/chapitre-1
|
||||
paras:
|
||||
p-0-8d27a7f5:
|
||||
refs:
|
||||
- url: https://auth.archicratie.trans-hands.synology.me/authenticated
|
||||
label: Lien web
|
||||
kind: (livre / article / vidéo / site / autre) Site
|
||||
ts: 2026-02-27T12:34:31.704Z
|
||||
fromIssue: 142
|
||||
@@ -1,9 +0,0 @@
|
||||
schema: 1
|
||||
page: archicrat-ia/chapitre-1
|
||||
paras:
|
||||
p-1-8a6c18bf:
|
||||
comments_editorial:
|
||||
- text: Yeaha
|
||||
status: new
|
||||
ts: 2026-02-27T12:40:39.462Z
|
||||
fromIssue: 143
|
||||
@@ -1,18 +0,0 @@
|
||||
schema: 1
|
||||
page: archicrat-ia/chapitre-3
|
||||
paras:
|
||||
p-0-ace27175:
|
||||
media:
|
||||
- type: image
|
||||
src: /media/archicrat-ia/chapitre-3/p-0-ace27175/Capture_d_e_cran_2025-05-05_a_19.20.40.png
|
||||
caption: "[Media] p-0-ace27175 — Chapitre 3 — Philosophies du pouvoir et
|
||||
archicration"
|
||||
credit: ""
|
||||
ts: 2026-02-27T12:43:14.259Z
|
||||
fromIssue: 144
|
||||
refs:
|
||||
- url: https://gitea.archicratie.trans-hands.synology.me
|
||||
label: Gitea
|
||||
kind: (livre / article / vidéo / site / autre) Site
|
||||
ts: 2026-03-02T19:53:21.252Z
|
||||
fromIssue: 169
|
||||
@@ -1,11 +0,0 @@
|
||||
schema: 1
|
||||
page: archicrat-ia/chapitre-3
|
||||
paras:
|
||||
p-1-60c7ea48:
|
||||
refs:
|
||||
- url: https://gitea.archicratie.trans-hands.synology.me
|
||||
label: Gitea
|
||||
kind: (livre / article / vidéo / site / autre) Site
|
||||
ts: 2026-03-02T20:01:55.858Z
|
||||
fromIssue: 172
|
||||
# testB: hotpatch-auto gate proof
|
||||
@@ -1,30 +0,0 @@
|
||||
schema: 1
|
||||
page: archicrat-ia/chapitre-4
|
||||
paras:
|
||||
p-2-31b12529:
|
||||
media:
|
||||
- type: image
|
||||
src: /media/archicrat-ia/chapitre-4/p-2-31b12529/Capture_d_e_cran_2026-02-16_a_13.05.58.png
|
||||
caption: "[Media] p-2-31b12529 — Chapitre 4 — Histoire archicratique des
|
||||
révolutions industrielles"
|
||||
credit: ""
|
||||
ts: 2026-02-25T18:58:32.359Z
|
||||
fromIssue: 115
|
||||
p-7-1da4a458:
|
||||
media:
|
||||
- type: image
|
||||
src: /media/archicrat-ia/chapitre-4/p-7-1da4a458/Capture_d_e_cran_2026-02-16_a_13.05.58.png
|
||||
caption: "[Media] p-7-1da4a458 — Chapitre 4 — Histoire archicratique des
|
||||
révolutions industrielles"
|
||||
credit: ""
|
||||
ts: 2026-02-25T19:11:32.634Z
|
||||
fromIssue: 121
|
||||
p-11-67c14c09:
|
||||
media:
|
||||
- type: image
|
||||
src: /media/archicrat-ia/chapitre-4/p-11-67c14c09/Capture_d_e_cran_2026-02-16_a_13.07.35.png
|
||||
caption: "[Media] p-11-67c14c09 — Chapitre 4 — Histoire archicratique des
|
||||
révolutions industrielles"
|
||||
credit: ""
|
||||
ts: 2026-02-26T13:17:41.286Z
|
||||
fromIssue: 129
|
||||
@@ -1,19 +0,0 @@
|
||||
schema: 1
|
||||
page: archicrat-ia/chapitre-4
|
||||
paras:
|
||||
p-11-67c14c09:
|
||||
media:
|
||||
- type: image
|
||||
src: /media/archicrat-ia/chapitre-4/p-11-67c14c09/Capture_d_e_cran_2026-02-16_a_13.07.35.png
|
||||
caption: "[Media] p-11-67c14c09 — Chapitre 4 — Histoire archicratique des
|
||||
révolutions industrielles"
|
||||
credit: ""
|
||||
ts: 2026-02-26T13:17:41.286Z
|
||||
fromIssue: 129
|
||||
- type: image
|
||||
src: /media/archicrat-ia/chapitre-4/p-11-67c14c09/Capture_d_e_cran_2025-05-05_a_19.20.40.png
|
||||
caption: "[Media] p-11-67c14c09 — Chapitre 4 — Histoire archicratique des
|
||||
révolutions industrielles"
|
||||
credit: ""
|
||||
ts: 2026-02-27T09:17:04.386Z
|
||||
fromIssue: 127
|
||||
@@ -1,50 +0,0 @@
|
||||
schema: 1
|
||||
|
||||
paras:
|
||||
p-0-d7974f88:
|
||||
refs:
|
||||
- label: "Happycratie — (Cabanas & Illouz) via Cairn"
|
||||
url: "https://shs.cairn.info/revue-ethnologie-francaise-2019-4-page-813?lang=fr"
|
||||
kind: "article"
|
||||
- label: "Techno-féodalisme — Variations (OpenEdition)"
|
||||
url: "https://journals.openedition.org/variations/2290"
|
||||
kind: "article"
|
||||
|
||||
authors:
|
||||
- "Eva Illouz"
|
||||
- "Yanis Varoufakis"
|
||||
|
||||
quotes:
|
||||
- text: "Dans Happycratie, Edgar Cabanas et Eva Illouz..."
|
||||
source: "Happycratie, p.1"
|
||||
- text: "En eux-mêmes, les actifs ne sont ni féodaux ni capitalistes..."
|
||||
source: "Entretien Morozov/Varoufakis — techno-féodalisme"
|
||||
|
||||
media:
|
||||
- type: "image"
|
||||
src: "/public/media/archicratie/archicrat-ia/prologue/p-0-d7974f88/schema-1.svg"
|
||||
caption: "Tableau explicatif"
|
||||
credit: "ChatGPT"
|
||||
- type: "image"
|
||||
src: "/public/media/archicratie/archicrat-ia/prologue/p-0-d7974f88/schema-2.svg"
|
||||
caption: "Diagramme d’évolution"
|
||||
credit: "Yanis Varoufakis"
|
||||
|
||||
comments_editorial:
|
||||
- text: "TODO: nuancer / préciser — commentaire éditorial versionné (pas public)."
|
||||
status: "draft"
|
||||
|
||||
p-1-2ef25f29:
|
||||
refs:
|
||||
- label: "Kafka et le pouvoir — Bernard Lahire (Cairn)"
|
||||
url: "https://shs.cairn.info/franz-kafka--9782707159410-page-475?lang=fr"
|
||||
kind: "book"
|
||||
|
||||
authors:
|
||||
- "Bernard Lahire"
|
||||
|
||||
quotes:
|
||||
- text: "Si l’on voulait chercher quelque chose comme une vision du monde chez Kafka..."
|
||||
source: "Bernard Lahire, Franz Kafka, p.475+"
|
||||
|
||||
comments_editorial: []
|
||||
@@ -25,12 +25,12 @@
|
||||
|
||||
{/* ✅ actions références en haut (niveau 2 uniquement) */}
|
||||
<div class="panel-top-actions level-2" aria-label="Actions références">
|
||||
<div class="panel-actions">
|
||||
<div class="panel-actions">
|
||||
<button class="panel-btn panel-btn--primary" id="panel-ref-submit" type="button">
|
||||
Soumettre une référence (Gitea)
|
||||
Soumettre une référence (Gitea)
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-msg" id="panel-ref-msg" hidden></div>
|
||||
</div>
|
||||
<div class="panel-msg" id="panel-ref-msg" hidden></div>
|
||||
</div>
|
||||
|
||||
<section class="panel-block level-2" aria-label="Références et auteurs">
|
||||
@@ -60,7 +60,7 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* ✅ Lightbox media (pop-up au-dessus du panel) */}
|
||||
{/* ✅ Lightbox media (plein écran) */}
|
||||
<div class="panel-lightbox" id="panel-lightbox" hidden aria-hidden="true">
|
||||
<div class="panel-lightbox__overlay" data-close="1"></div>
|
||||
<div class="panel-lightbox__dialog" role="dialog" aria-modal="true" aria-label="Aperçu du média">
|
||||
@@ -93,6 +93,9 @@
|
||||
const btnMediaSubmit = root.querySelector("#panel-media-submit");
|
||||
const msgMedia = root.querySelector("#panel-media-msg");
|
||||
|
||||
const btnRefSubmit = root.querySelector("#panel-ref-submit");
|
||||
const msgRef = root.querySelector("#panel-ref-msg");
|
||||
|
||||
const taComment = root.querySelector("#panel-comment-text");
|
||||
const btnSend = root.querySelector("#panel-comment-send");
|
||||
const msgComment = root.querySelector("#panel-comment-msg");
|
||||
@@ -101,9 +104,6 @@
|
||||
const lbContent = root.querySelector("#panel-lightbox-content");
|
||||
const lbCaption = root.querySelector("#panel-lightbox-caption");
|
||||
|
||||
const btnRefSubmit = root.querySelector("#panel-ref-submit");
|
||||
const msgRef = root.querySelector("#panel-ref-msg");
|
||||
|
||||
const docTitle = document.body?.dataset?.docTitle || document.title || "Archicratie";
|
||||
const docVersion = document.body?.dataset?.docVersion || "";
|
||||
|
||||
@@ -114,6 +114,16 @@
|
||||
let currentParaId = "";
|
||||
let mediaShowAll = (localStorage.getItem("archicratie:panel:mediaAll") === "1");
|
||||
|
||||
// ===== cosmetics: micro flash “update” =====
|
||||
let _flashT = 0;
|
||||
function flashUpdate(){
|
||||
try {
|
||||
root.classList.add("is-updating");
|
||||
if (_flashT) clearTimeout(_flashT);
|
||||
_flashT = setTimeout(() => root.classList.remove("is-updating"), 180);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// ===== globals =====
|
||||
function getG() {
|
||||
return window.__archiGitea || { ready: false, base: "", owner: "", repo: "" };
|
||||
@@ -121,9 +131,6 @@
|
||||
function getAuthInfoP() {
|
||||
return window.__archiAuthInfoP || Promise.resolve({ ok: false, groups: [] });
|
||||
}
|
||||
function isDev() {
|
||||
return Boolean((window.__archiFlags && window.__archiFlags.dev) || /^(localhost|127\.0\.0\.1|\[::1\])$/i.test(location.hostname));
|
||||
}
|
||||
|
||||
const access = { ready: false, canUsers: false };
|
||||
|
||||
@@ -137,8 +144,7 @@
|
||||
return Array.isArray(groups) && groups.some((x) => String(x).toLowerCase() === gg);
|
||||
}
|
||||
|
||||
// ✅ règle mission : readers + editors peuvent soumettre médias + commentaires
|
||||
// ✅ dev fallback : si /_auth/whoami n’existe pas, on autorise pour tester
|
||||
// ✅ readers + editors peuvent soumettre médias + commentaires + refs
|
||||
getAuthInfoP().then((info) => {
|
||||
const groups = Array.isArray(info?.groups) ? info.groups : [];
|
||||
const canReaders = inGroup(groups, "readers");
|
||||
@@ -152,7 +158,6 @@
|
||||
if (btnSend) btnSend.disabled = !access.canUsers;
|
||||
if (btnRefSubmit) btnRefSubmit.disabled = !access.canUsers;
|
||||
|
||||
// si pas d'accès, on informe (soft)
|
||||
if (!access.canUsers) {
|
||||
if (msgHead) {
|
||||
msgHead.hidden = false;
|
||||
@@ -161,7 +166,6 @@
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
// fallback dev (cohérent: media + ref + comment)
|
||||
access.ready = true;
|
||||
if (Boolean(window.__archiFlags && window.__archiFlags.whoamiSkipped)) {
|
||||
access.canUsers = true;
|
||||
@@ -213,7 +217,6 @@
|
||||
const res = await fetch("/annotations-index.json?_=" + Date.now(), { cache: "no-store" });
|
||||
if (res && res.ok) return await res.json();
|
||||
} catch {}
|
||||
// ✅ antifragile: ne pas “cacher” un échec pour toujours (dev/HMR/boot race)
|
||||
_idxP = null;
|
||||
return null;
|
||||
})();
|
||||
@@ -255,24 +258,22 @@
|
||||
return issue.toString();
|
||||
}
|
||||
|
||||
// Ouvre un nouvel onglet UNE SEULE FOIS (évite le double-open Safari/Firefox + noopener).
|
||||
function openNewTab(url) {
|
||||
try {
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.target = "_blank";
|
||||
a.rel = "noopener noreferrer";
|
||||
a.style.display = "none";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
return true; // on ne peut pas détecter proprement un blocage sans retomber dans le double-open
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
function openNewTab(url) {
|
||||
try {
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.target = "_blank";
|
||||
a.rel = "noopener noreferrer";
|
||||
a.style.display = "none";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ====== GARDES ANTI-DOUBLONS ======
|
||||
const _openStamp = new Map();
|
||||
function openOnce(key, fn) {
|
||||
const now = Date.now();
|
||||
@@ -301,13 +302,21 @@
|
||||
}
|
||||
|
||||
// ===== Lightbox =====
|
||||
function lockScroll(on) {
|
||||
try {
|
||||
document.documentElement.classList.toggle("archi-lb-open", !!on);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function closeLightbox() {
|
||||
if (!lb) return;
|
||||
lb.hidden = true;
|
||||
lb.setAttribute("aria-hidden", "true");
|
||||
if (lbContent) clear(lbContent);
|
||||
if (lbCaption) { lbCaption.hidden = true; lbCaption.textContent = ""; }
|
||||
lockScroll(false);
|
||||
}
|
||||
|
||||
function openLightbox({ type, src, caption }) {
|
||||
if (!lb || !lbContent) return;
|
||||
clear(lbContent);
|
||||
@@ -346,6 +355,7 @@
|
||||
else { lbCaption.hidden = true; lbCaption.textContent = ""; }
|
||||
}
|
||||
|
||||
lockScroll(true);
|
||||
lb.hidden = false;
|
||||
lb.setAttribute("aria-hidden", "false");
|
||||
}
|
||||
@@ -363,7 +373,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ===== Renders =====
|
||||
function renderLevel2(data) {
|
||||
clear(elL2);
|
||||
if (!elL2) return;
|
||||
@@ -563,13 +572,16 @@
|
||||
async function updatePanel(paraId) {
|
||||
currentParaId = paraId || currentParaId || "";
|
||||
if (elId) elId.textContent = currentParaId || "—";
|
||||
|
||||
flashUpdate();
|
||||
|
||||
hideMsg(msgHead);
|
||||
hideMsg(msgMedia);
|
||||
hideMsg(msgComment);
|
||||
hideMsg(msgRef);
|
||||
|
||||
const idx = await loadIndex();
|
||||
|
||||
// ✅ message soft si l’index est indisponible (sans écraser le message d’auth)
|
||||
if (!idx && msgHead && msgHead.hidden) {
|
||||
msgHead.hidden = false;
|
||||
msgHead.textContent = "Index annotations indisponible (annotations-index.json).";
|
||||
@@ -583,7 +595,6 @@
|
||||
renderLevel4(data);
|
||||
}
|
||||
|
||||
// ===== media "voir tous" =====
|
||||
if (btnMediaAll) {
|
||||
bindClickOnce(btnMediaAll, (ev) => {
|
||||
ev.preventDefault();
|
||||
@@ -595,7 +606,6 @@
|
||||
btnMediaAll.textContent = mediaShowAll ? "Réduire la liste" : "Voir tous les éléments";
|
||||
}
|
||||
|
||||
// ===== media submit (readers + editors) =====
|
||||
if (btnMediaSubmit) {
|
||||
bindClickOnce(btnMediaSubmit, (ev) => {
|
||||
ev.preventDefault();
|
||||
@@ -638,27 +648,26 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ===== référence submit (readers + editors) =====
|
||||
if (btnRefSubmit) {
|
||||
if (btnRefSubmit) {
|
||||
bindClickOnce(btnRefSubmit, (ev) => {
|
||||
ev.preventDefault();
|
||||
hideMsg(msgRef);
|
||||
ev.preventDefault();
|
||||
hideMsg(msgRef);
|
||||
|
||||
if (guardEventOnce(ev, "gitea_open_ref")) return;
|
||||
if (guardEventOnce(ev, "gitea_open_ref")) return;
|
||||
|
||||
if (!currentParaId) return showMsg(msgRef, "Choisis d’abord un paragraphe (scroll / survol).", "warn");
|
||||
if (!getG().ready) return showMsg(msgRef, "Gitea non configuré (PUBLIC_GITEA_*).", "error");
|
||||
if (btnRefSubmit.disabled) return showMsg(msgRef, "Connexion requise (readers/editors).", "error");
|
||||
if (!currentParaId) return showMsg(msgRef, "Choisis d’abord un paragraphe (scroll / survol).", "warn");
|
||||
if (!getG().ready) return showMsg(msgRef, "Gitea non configuré (PUBLIC_GITEA_*).", "error");
|
||||
if (btnRefSubmit.disabled) return showMsg(msgRef, "Connexion requise (readers/editors).", "error");
|
||||
|
||||
const pageUrl = new URL(location.href);
|
||||
pageUrl.search = "";
|
||||
pageUrl.hash = currentParaId;
|
||||
const pageUrl = new URL(location.href);
|
||||
pageUrl.search = "";
|
||||
pageUrl.hash = currentParaId;
|
||||
|
||||
const paraTxt = getParaText(currentParaId);
|
||||
const excerpt = paraTxt.length > FULL_TEXT_SOFT_LIMIT ? (paraTxt.slice(0, FULL_TEXT_SOFT_LIMIT) + "…") : paraTxt;
|
||||
const paraTxt = getParaText(currentParaId);
|
||||
const excerpt = paraTxt.length > FULL_TEXT_SOFT_LIMIT ? (paraTxt.slice(0, FULL_TEXT_SOFT_LIMIT) + "…") : paraTxt;
|
||||
|
||||
const title = `[Reference] ${currentParaId} — ${docTitle}`;
|
||||
const body = [
|
||||
const title = `[Reference] ${currentParaId} — ${docTitle}`;
|
||||
const body = [
|
||||
`Chemin: ${location.pathname}`,
|
||||
`URL: ${pageUrl.toString()}`,
|
||||
`Ancre: #${currentParaId}`,
|
||||
@@ -676,18 +685,16 @@
|
||||
``,
|
||||
`---`,
|
||||
`Note: issue générée depuis le site (pré-remplissage).`,
|
||||
].join("\n");
|
||||
].join("\n");
|
||||
|
||||
const url = buildIssueURL({ title, body });
|
||||
if (!url) return showMsg(msgRef, "Impossible de générer l’issue.", "error");
|
||||
const url = buildIssueURL({ title, body });
|
||||
if (!url) return showMsg(msgRef, "Impossible de générer l’issue.", "error");
|
||||
|
||||
const ok = openOnce(`ref:${currentParaId}`, () => openNewTab(url));
|
||||
if (!ok) showMsg(msgRef, "Si rien ne s’ouvre : autorise les popups pour ce site.", "error");
|
||||
const ok = openOnce(`ref:${currentParaId}`, () => openNewTab(url));
|
||||
if (!ok) showMsg(msgRef, "Si rien ne s’ouvre : autorise les popups pour ce site.", "error");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===== commentaire (readers + editors) =====
|
||||
if (btnSend) {
|
||||
bindClickOnce(btnSend, (ev) => {
|
||||
ev.preventDefault();
|
||||
@@ -739,60 +746,31 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ===== wiring: para courant (aligné sur le paragraphe sous le reading-follow) =====
|
||||
function isPara(el) {
|
||||
return Boolean(el && el.nodeType === 1 && el.matches && el.matches('.reading p[id^="p-"]'));
|
||||
// ===== wiring: para courant (SOURCE OF TRUTH = EditionLayout) =====
|
||||
function onCurrentPara(ev) {
|
||||
try {
|
||||
const id = ev?.detail?.id ? String(ev.detail.id) : "";
|
||||
if (!id || !/^p-\d+-/i.test(id)) return;
|
||||
if (id === currentParaId) return;
|
||||
updatePanel(id);
|
||||
} catch {}
|
||||
}
|
||||
window.addEventListener("archicratie:currentPara", onCurrentPara);
|
||||
|
||||
function pickParaAtY(y) {
|
||||
const x = Math.max(0, Math.round(window.innerWidth * 0.5));
|
||||
const candidates = [
|
||||
document.elementFromPoint(x, y),
|
||||
document.elementFromPoint(Math.min(window.innerWidth - 1, x + 60), y),
|
||||
document.elementFromPoint(Math.max(0, x - 60), y),
|
||||
].filter(Boolean);
|
||||
const initial = String(location.hash || "").replace(/^#/, "").trim();
|
||||
|
||||
for (const c of candidates) {
|
||||
if (isPara(c)) return c;
|
||||
const p = c.closest ? c.closest('.reading p[id^="p-"]') : null;
|
||||
if (isPara(p)) return p;
|
||||
}
|
||||
return null;
|
||||
if (/^p-\d+-/i.test(initial)) {
|
||||
updatePanel(initial);
|
||||
} else if (window.__archiCurrentParaId && /^p-\d+-/i.test(String(window.__archiCurrentParaId))) {
|
||||
updatePanel(String(window.__archiCurrentParaId));
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const id = String(window.__archiCurrentParaId || "").trim();
|
||||
if (/^p-\d+-/i.test(id)) updatePanel(id);
|
||||
} catch {}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
let _lastPicked = "";
|
||||
function syncFromFollowLine() {
|
||||
const off = Number(document.documentElement.style.getPropertyValue("--sticky-offset-px")) || 0;
|
||||
const y = Math.round(off + 8);
|
||||
const p = pickParaAtY(y);
|
||||
if (!p || !p.id) return;
|
||||
if (p.id === _lastPicked) return;
|
||||
_lastPicked = p.id;
|
||||
|
||||
// met à jour l'app global (EditionLayout écoute déjà currentPara)
|
||||
try { window.dispatchEvent(new CustomEvent("archicratie:currentPara", { detail: { id: p.id } })); } catch {}
|
||||
|
||||
// et met à jour le panel immédiatement (sans attendre)
|
||||
updatePanel(p.id);
|
||||
}
|
||||
|
||||
let ticking = false;
|
||||
function onScroll() {
|
||||
if (ticking) return;
|
||||
ticking = true;
|
||||
requestAnimationFrame(() => {
|
||||
ticking = false;
|
||||
syncFromFollowLine();
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
window.addEventListener("resize", onScroll);
|
||||
|
||||
// Initial: hash > sinon calc
|
||||
const initial = String(location.hash || "").replace(/^#/, "");
|
||||
if (/^p-\d+-/i.test(initial)) updatePanel(initial);
|
||||
else setTimeout(() => { try { syncFromFollowLine(); } catch {} }, 0);
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -805,6 +783,8 @@
|
||||
position: sticky;
|
||||
top: calc(var(--sticky-header-h) + var(--page-gap));
|
||||
align-self: start;
|
||||
|
||||
--thumb: 92px; /* ✅ taille des vignettes (80–110 selon goût) */
|
||||
}
|
||||
|
||||
:global(body[data-reading-level="3"]) .page-panel{
|
||||
@@ -922,28 +902,33 @@
|
||||
/* actions médias en haut */
|
||||
.panel-top-actions{ margin-top: 8px; }
|
||||
|
||||
/* ===== media thumbnails (150x150) ===== */
|
||||
/* ===== media thumbnails (plus petits + plus denses) ===== */
|
||||
.panel-media-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--thumb), 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.panel-media-tile{
|
||||
width: 150px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
border: 1px solid rgba(127,127,127,.20);
|
||||
border-radius: 14px;
|
||||
padding: 8px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
|
||||
}
|
||||
|
||||
.panel-media-tile:hover{
|
||||
transform: translateY(-1px);
|
||||
background: rgba(127,127,127,0.07);
|
||||
border-color: rgba(127,127,127,.32);
|
||||
}
|
||||
|
||||
.panel-media-tile img{
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
height: var(--thumb);
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
@@ -951,8 +936,8 @@
|
||||
}
|
||||
|
||||
.panel-media-ph{
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
width: 100%;
|
||||
height: var(--thumb);
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -995,7 +980,11 @@
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* ===== Lightbox ===== */
|
||||
/* ===== Lightbox (plein écran “cinéma”) ===== */
|
||||
:global(html.archi-lb-open){
|
||||
overflow: hidden; /* ✅ empêche le scroll derrière */
|
||||
}
|
||||
|
||||
.panel-lightbox{
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -1005,58 +994,66 @@
|
||||
.panel-lightbox__overlay{
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.80);
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
background: rgba(0,0,0,0.84);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.panel-lightbox__dialog{
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
top: calc(var(--sticky-header-h) + 16px);
|
||||
width: min(520px, calc(100vw - 48px));
|
||||
max-height: calc(100vh - (var(--sticky-header-h) + 32px));
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
width: min(1100px, 92vw);
|
||||
max-height: 92vh;
|
||||
overflow: auto;
|
||||
|
||||
border: 1px solid rgba(127,127,127,0.22);
|
||||
border-radius: 16px;
|
||||
background: rgba(255,255,255,0.10);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
padding: 12px;
|
||||
}
|
||||
border: 1px solid rgba(255,255,255,0.14);
|
||||
border-radius: 18px;
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-lightbox__dialog{
|
||||
background: rgba(0,0,0,0.28);
|
||||
}
|
||||
background: rgba(20,20,20,0.55);
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
|
||||
padding: 16px;
|
||||
box-shadow: 0 24px 70px rgba(0,0,0,0.55);
|
||||
}
|
||||
|
||||
.panel-lightbox__close{
|
||||
position: sticky;
|
||||
top: 0;
|
||||
margin-left: auto;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 34px;
|
||||
height: 30px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(127,127,127,0.35);
|
||||
background: rgba(127,127,127,0.10);
|
||||
|
||||
width: 44px;
|
||||
height: 40px;
|
||||
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(255,255,255,0.22);
|
||||
background: rgba(255,255,255,0.10);
|
||||
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.panel-lightbox__content{
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.panel-lightbox__content img,
|
||||
.panel-lightbox__content video{
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
border-radius: 12px;
|
||||
max-height: calc(92vh - 160px);
|
||||
object-fit: contain;
|
||||
|
||||
background: rgba(0,0,0,0.22);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.panel-lightbox__content audio{
|
||||
@@ -1064,13 +1061,14 @@
|
||||
}
|
||||
|
||||
.panel-lightbox__caption{
|
||||
margin-top: 10px;
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
opacity: .92;
|
||||
color: rgba(255,255,255,0.92);
|
||||
}
|
||||
|
||||
@media (max-width: 1100px){
|
||||
.page-panel{ display: none; }
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -5,6 +5,7 @@ import LevelToggle from "../components/LevelToggle.astro";
|
||||
import BuildStamp from "../components/BuildStamp.astro";
|
||||
import SidePanel from "../components/SidePanel.astro";
|
||||
import "../styles/global.css";
|
||||
import "../styles/panel.css";
|
||||
|
||||
const {
|
||||
title,
|
||||
@@ -261,17 +262,24 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
padding: var(--page-gap) 16px 48px;
|
||||
}
|
||||
|
||||
/* ✅ Grille robustifiée + panel plus confortable (base) */
|
||||
.page-shell{
|
||||
--aside-w: 320px;
|
||||
--reading-w: 78ch;
|
||||
--panel-w: 380px;
|
||||
--panel-w: 420px;
|
||||
--gap: 18px;
|
||||
|
||||
max-width: calc(var(--aside-w) + var(--reading-w) + var(--panel-w) + (var(--gap) * 2));
|
||||
max-width: min(
|
||||
calc(var(--aside-w) + var(--reading-w) + var(--panel-w) + (var(--gap) * 2)),
|
||||
calc(100vw - 32px)
|
||||
);
|
||||
margin: 0 auto;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: var(--aside-w) minmax(0, var(--reading-w)) var(--panel-w);
|
||||
grid-template-columns:
|
||||
var(--aside-w)
|
||||
minmax(0, var(--reading-w))
|
||||
minmax(0, var(--panel-w));
|
||||
column-gap: var(--gap);
|
||||
align-items: start;
|
||||
}
|
||||
@@ -346,23 +354,38 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
}
|
||||
:global(body[data-reading-level="1"]) .page-panel{ display: none !important; }
|
||||
|
||||
/* Niveau 3 : lecture plus étroite + panel large ; TOC masqué */
|
||||
/* Niveau 3 : lecture plus étroite + panel LARGE ; TOC masqué */
|
||||
:global(body[data-reading-level="3"]) .page-shell{
|
||||
--reading-w: 62ch;
|
||||
--panel-w: 560px;
|
||||
max-width: calc(var(--reading-w) + var(--panel-w) + (var(--gap) * 1));
|
||||
grid-template-columns: minmax(0, var(--reading-w)) var(--panel-w);
|
||||
--reading-w: 60ch;
|
||||
--panel-w: clamp(720px, 48vw, 940px);
|
||||
|
||||
max-width: min(
|
||||
calc(var(--reading-w) + var(--panel-w) + var(--gap)),
|
||||
calc(100vw - 32px)
|
||||
);
|
||||
|
||||
grid-template-columns:
|
||||
minmax(0, var(--reading-w))
|
||||
minmax(0, var(--panel-w));
|
||||
}
|
||||
:global(body[data-reading-level="3"]) .page-aside{ display: none; }
|
||||
:global(body[data-reading-level="3"]) .reading{ grid-column: 1; }
|
||||
|
||||
/* Niveau 4 : TOC à gauche + panel */
|
||||
/* Niveau 4 : TOC à gauche + panel confortable */
|
||||
:global(body[data-reading-level="4"]) .page-shell{
|
||||
--aside-w: 320px;
|
||||
--reading-w: 64ch;
|
||||
--panel-w: 520px;
|
||||
max-width: calc(var(--aside-w) + var(--reading-w) + var(--panel-w) + (var(--gap) * 2));
|
||||
grid-template-columns: var(--aside-w) minmax(0, var(--reading-w)) var(--panel-w);
|
||||
--panel-w: clamp(520px, 36vw, 720px);
|
||||
|
||||
max-width: min(
|
||||
calc(var(--aside-w) + var(--reading-w) + var(--panel-w) + (var(--gap) * 2)),
|
||||
calc(100vw - 32px)
|
||||
);
|
||||
|
||||
grid-template-columns:
|
||||
var(--aside-w)
|
||||
minmax(0, var(--reading-w))
|
||||
minmax(0, var(--panel-w));
|
||||
}
|
||||
:global(body[data-reading-level="4"]) .page-aside{ display: block; }
|
||||
:global(body[data-reading-level="4"]) .reading{ grid-column: 2; }
|
||||
@@ -1158,6 +1181,45 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
});
|
||||
});
|
||||
|
||||
/* ✅ NOUVEAU : clic paragraphe => snap sous reading-follow + SidePanel aligné */
|
||||
safe("click-para-align", () => {
|
||||
if (!reading) return;
|
||||
|
||||
reading.addEventListener("click", (ev) => {
|
||||
try {
|
||||
const t = ev.target;
|
||||
|
||||
// ne pas casser les boutons / liens / para-tools
|
||||
if (t && t.closest && t.closest(".para-tools")) return;
|
||||
const a = t && t.closest ? t.closest("a") : null;
|
||||
if (a) return;
|
||||
|
||||
const p = t && t.closest ? t.closest('.reading p[id^="p-"]') : null;
|
||||
if (!p || !p.id) return;
|
||||
|
||||
// si sélection texte en cours => ne pas snap
|
||||
const sel = window.getSelection ? window.getSelection() : null;
|
||||
if (sel && !sel.isCollapsed) return;
|
||||
|
||||
openDetailsIfNeeded(p);
|
||||
|
||||
// snap immédiat sous followbar
|
||||
scrollToElWithOffset(p, 12, "auto");
|
||||
|
||||
// URL stable
|
||||
history.replaceState(null, "", `${location.pathname}#${p.id}`);
|
||||
|
||||
// force la source de vérité (SidePanel suit via event archicratie:currentPara)
|
||||
__hoverParaId = "";
|
||||
__activeParaId = p.id;
|
||||
setCurrentPara(p.id, "click");
|
||||
|
||||
writeLastSeen(p.id);
|
||||
updateResumeButton();
|
||||
} catch {}
|
||||
}, { passive: true });
|
||||
});
|
||||
|
||||
// ===== Hash handling (robuste) =====
|
||||
function resolveParaIdFromEl(el) {
|
||||
if (!el) return "";
|
||||
@@ -1281,8 +1343,6 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
|
||||
const h1 = reading.querySelector("h1");
|
||||
|
||||
// ✅ FIX: ne pas dépendre du tag <span>; accepter .details-anchor quel que soit le tag,
|
||||
// et utiliser <details> comme marker (top fiable) pour que rf-h2 s'affiche correctement.
|
||||
const h2Anchors = Array.from(reading.querySelectorAll(".details-anchor[id]"))
|
||||
.map((s) => {
|
||||
const d = (s.nextElementSibling && s.nextElementSibling.tagName === "DETAILS")
|
||||
@@ -1572,4 +1632,4 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -103,10 +103,16 @@ export const GET: APIRoute = async () => {
|
||||
const errors: Array<{ file: string; error: string }> = [];
|
||||
|
||||
let files: string[] = [];
|
||||
let missingRoot = false;
|
||||
|
||||
try {
|
||||
files = await walk(ANNO_ROOT);
|
||||
} catch (e: any) {
|
||||
throw new Error(`Missing annotations root: ${ANNO_ROOT} (${e?.message || e})`);
|
||||
// ✅ FAIL-OPEN : pas d’annotations => index vide (ne casse pas la build)
|
||||
missingRoot = true;
|
||||
console.warn(`[annotations-index] Missing annotations root: ${ANNO_ROOT} (${e?.message || e})`);
|
||||
files = [];
|
||||
// ✅ surtout PAS d'errors.push ici
|
||||
}
|
||||
|
||||
for (const fp of files) {
|
||||
@@ -177,6 +183,14 @@ export const GET: APIRoute = async () => {
|
||||
pg.paras = next;
|
||||
}
|
||||
|
||||
const warnings: Array<{ where: string; warning: string }> = [];
|
||||
if (missingRoot) {
|
||||
warnings.push({
|
||||
where: "src/pages/annotations-index.json.ts",
|
||||
warning: `Missing annotations root "${ANNO_ROOT}" (treated as empty).`,
|
||||
});
|
||||
}
|
||||
|
||||
const out = {
|
||||
schema: 1,
|
||||
generatedAt: new Date().toISOString(),
|
||||
@@ -186,10 +200,13 @@ export const GET: APIRoute = async () => {
|
||||
paras: Object.values(pages).reduce((n, p) => n + Object.keys(p.paras || {}).length, 0),
|
||||
errors: errors.length,
|
||||
},
|
||||
warnings,
|
||||
errors,
|
||||
};
|
||||
|
||||
if (errors.length) {
|
||||
// ✅ FAIL-OPEN uniquement si le dossier manque.
|
||||
// Si le dossier existe mais qu’un YAML est cassé -> fail-closed.
|
||||
if (errors.length && !missingRoot) {
|
||||
throw new Error(`${errors[0].file}: ${errors[0].error}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,17 +27,40 @@
|
||||
--rf-h3-size: 1.25rem;
|
||||
--rf-h3-lh: 1.25;
|
||||
--rf-h3-fw: 650;
|
||||
|
||||
/* ===== Polish (non destructif) ===== */
|
||||
--ease-out: cubic-bezier(.2,.9,.2,1);
|
||||
--ease-soft: cubic-bezier(.2,.8,.2,1);
|
||||
|
||||
--focus-ring: 2px solid rgba(140,140,255,0.60);
|
||||
--focus-ring-offset: 2px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
|
||||
/* polish */
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a { text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
/* ===== Focus (accessibilité + feel premium) ===== */
|
||||
:focus { outline: none; }
|
||||
:focus-visible{
|
||||
outline: var(--focus-ring);
|
||||
outline-offset: var(--focus-ring-offset);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce){
|
||||
*{ transition: none !important; animation: none !important; }
|
||||
}
|
||||
|
||||
/* Header sticky */
|
||||
header{
|
||||
position: sticky;
|
||||
@@ -61,15 +84,26 @@ main { padding: 0; }
|
||||
.reading {
|
||||
max-width: 78ch;
|
||||
margin: 0 auto;
|
||||
|
||||
/* polish */
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.005em;
|
||||
}
|
||||
|
||||
.reading h1 {
|
||||
line-height: 1.2;
|
||||
margin: 0 0 10px;
|
||||
|
||||
/* polish */
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.reading p { margin: 0 0 12px; }
|
||||
|
||||
/* petits ajustements de respiration */
|
||||
.reading h2 { margin-top: 18px; }
|
||||
.reading h3 { margin-top: 14px; }
|
||||
|
||||
.edition-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -99,6 +133,11 @@ main { padding: 0; }
|
||||
border: 1px solid rgba(127,127,127,0.55);
|
||||
border-radius: 999px;
|
||||
padding: 5px 12px;
|
||||
transition: transform 120ms var(--ease-out), background 120ms var(--ease-out);
|
||||
}
|
||||
.resume-btn:hover{
|
||||
background: rgba(127,127,127,0.10);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Jump by paragraph id */
|
||||
@@ -114,6 +153,11 @@ main { padding: 0; }
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
width: 320px;
|
||||
transition: border-color 120ms var(--ease-out), box-shadow 120ms var(--ease-out);
|
||||
}
|
||||
.jump-input:focus-visible{
|
||||
border-color: rgba(140,140,255,0.65);
|
||||
box-shadow: 0 0 0 3px rgba(140,140,255,0.18);
|
||||
}
|
||||
.jump-input.is-error{
|
||||
outline: 2px solid rgba(127,127,127,0.55);
|
||||
@@ -126,6 +170,11 @@ main { padding: 0; }
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: transform 120ms var(--ease-out), background 120ms var(--ease-out);
|
||||
}
|
||||
.jump-btn:hover{
|
||||
background: rgba(127,127,127,0.10);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Toggle niveaux (legacy, non bloquant) */
|
||||
@@ -137,6 +186,11 @@ main { padding: 0; }
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: transform 120ms var(--ease-out), background 120ms var(--ease-out), border-color 120ms var(--ease-out);
|
||||
}
|
||||
.lvl-btn:hover{
|
||||
background: rgba(127,127,127,0.10);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.lvl-btn[aria-pressed="true"] {
|
||||
border-color: rgba(127,127,127,0.9);
|
||||
@@ -211,6 +265,13 @@ body[data-reading-level="4"] .level-3 { display: none; }
|
||||
border-radius: 999px;
|
||||
padding: 2px 8px;
|
||||
background: transparent;
|
||||
transition: transform 120ms var(--ease-out), background 120ms var(--ease-out);
|
||||
}
|
||||
.para-anchor:hover,
|
||||
.para-propose:hover,
|
||||
.para-cite:hover{
|
||||
background: rgba(127,127,127,0.10);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.para-cite{ cursor: pointer; }
|
||||
|
||||
@@ -221,8 +282,13 @@ body[data-reading-level="4"] .level-3 { display: none; }
|
||||
padding: 4px 12px;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
transition: transform 120ms var(--ease-out), background 120ms var(--ease-out);
|
||||
}
|
||||
.para-bookmark:hover{
|
||||
background: rgba(127,127,127,0.10);
|
||||
transform: translateY(-1px);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.para-bookmark:hover{ text-decoration: underline; }
|
||||
|
||||
/* Highlight (jump / resume / arrivée hash) */
|
||||
.para-highlight{
|
||||
@@ -420,3 +486,57 @@ html{ scroll-behavior: smooth; }
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* ==========================
|
||||
Landscape boost (mobile/tablet)
|
||||
CSS-only, non destructif
|
||||
- iOS + Android
|
||||
- élargit la lecture en paysage sur petits écrans
|
||||
- ne touche pas au desktop
|
||||
========================== */
|
||||
|
||||
/* 1) Quand on est en paysage sur “petits” devices,
|
||||
on évite une colonne unique trop “étroite” et on retire le max-width 78ch. */
|
||||
@media (orientation: landscape) and (max-width: 1024px){
|
||||
/* La grille (EditionLayout) utilise .page-shell ; on la rend plus “fluide” */
|
||||
.page-shell{
|
||||
max-width: calc(100vw - 16px) !important; /* respire mais exploite la largeur */
|
||||
}
|
||||
|
||||
/* La lecture ne doit pas rester “bridée” à 78ch en paysage */
|
||||
.reading{
|
||||
max-width: none !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2) Cas extrême : petits téléphones en paysage (hauteur faible).
|
||||
On réduit légèrement les paddings pour gagner de la place utile. */
|
||||
@media (orientation: landscape) and (max-width: 820px) and (max-height: 520px){
|
||||
header{
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
/* allège un peu la barre d’édition (sans la casser) */
|
||||
.edition-bar{
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
gap: 8px 12px;
|
||||
}
|
||||
|
||||
/* champ jump un peu moins large, pour éviter de “manger” la nav */
|
||||
.jump-input{
|
||||
width: min(260px, 48vw);
|
||||
}
|
||||
}
|
||||
|
||||
/* 3) iOS Safari : le viewport peut être “bizarre” en paysage (barres).
|
||||
Cette règle aide à éviter des layouts coincés quand l’UI Safari bouge. */
|
||||
@supports (height: 100dvh){
|
||||
@media (orientation: landscape) and (max-width: 1024px){
|
||||
.page-shell{
|
||||
min-height: 100dvh;
|
||||
}
|
||||
}
|
||||
}
|
||||
568
src/styles/panel.css
Normal file
568
src/styles/panel.css
Normal file
@@ -0,0 +1,568 @@
|
||||
/* src/styles/panel.css — v3.1 premium FIX
|
||||
Objectifs :
|
||||
- Lightbox AU-DESSUS du header (pas masquée)
|
||||
- bouton close toujours visible
|
||||
- le haut du média jamais coupé
|
||||
- CSS-only, aucune logique JS modifiée
|
||||
*/
|
||||
|
||||
/* ===== Tokens ===== */
|
||||
:root{
|
||||
--panel-radius: 18px;
|
||||
--panel-radius-sm: 14px;
|
||||
|
||||
--panel-border: rgba(127,127,127,0.16);
|
||||
--panel-border-strong: rgba(127,127,127,0.26);
|
||||
|
||||
--panel-glass: rgba(255,255,255,0.055);
|
||||
--panel-glass-2: rgba(255,255,255,0.075);
|
||||
|
||||
--panel-shadow: 0 14px 34px rgba(0,0,0,0.07);
|
||||
--panel-shadow-2: 0 22px 60px rgba(0,0,0,0.10);
|
||||
|
||||
--panel-title: rgba(20,20,20,0.90);
|
||||
--panel-text: rgba(20,20,20,0.84);
|
||||
--panel-muted: rgba(20,20,20,0.62);
|
||||
|
||||
--chip-bg: rgba(127,127,127,0.10);
|
||||
--chip-bd: rgba(127,127,127,0.22);
|
||||
|
||||
--btn-bg: rgba(127,127,127,0.10);
|
||||
--btn-bd: rgba(127,127,127,0.30);
|
||||
|
||||
/* thumbs */
|
||||
--thumb: 84px;
|
||||
|
||||
/* Motion */
|
||||
--ease: cubic-bezier(.2,.85,.2,1);
|
||||
--ease2: cubic-bezier(.18,.9,.18,1);
|
||||
|
||||
/* Lightbox sizing */
|
||||
--lb-maxw: 1480px;
|
||||
|
||||
/* Inset (viewport safe) : header + notch + marges */
|
||||
--lb-inset-x: 14px;
|
||||
--lb-inset-top: max(calc(env(safe-area-inset-top, 0px) + 12px), 12px);
|
||||
--lb-inset-bot: max(calc(env(safe-area-inset-bottom, 0px) + 12px), 12px);
|
||||
|
||||
/* Space reserved for close button strip inside dialog */
|
||||
--lb-topbar-h: 56px;
|
||||
|
||||
/* padding viewer */
|
||||
--lb-pad: 16px;
|
||||
}
|
||||
|
||||
@media (min-width: 900px){
|
||||
:root{ --lb-inset-x: 24px; --lb-pad: 18px; }
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
:root{
|
||||
--panel-border: rgba(255,255,255,0.12);
|
||||
--panel-border-strong: rgba(255,255,255,0.18);
|
||||
|
||||
--panel-glass: rgba(255,255,255,0.045);
|
||||
--panel-glass-2: rgba(255,255,255,0.070);
|
||||
|
||||
--panel-shadow: 0 18px 48px rgba(0,0,0,0.26);
|
||||
--panel-shadow-2: 0 30px 80px rgba(0,0,0,0.38);
|
||||
|
||||
--panel-title: rgba(255,255,255,0.90);
|
||||
--panel-text: rgba(255,255,255,0.86);
|
||||
--panel-muted: rgba(255,255,255,0.62);
|
||||
|
||||
--chip-bg: rgba(255,255,255,0.08);
|
||||
--chip-bd: rgba(255,255,255,0.14);
|
||||
|
||||
--btn-bg: rgba(255,255,255,0.07);
|
||||
--btn-bd: rgba(255,255,255,0.16);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Panel container ===== */
|
||||
body[data-reading-level="1"] .page-panel{ display: none !important; }
|
||||
|
||||
/* IMPORTANT :
|
||||
Pour que la Lightbox (enfant du panel) puisse passer AU-DESSUS du header,
|
||||
on met le panel dans une couche au-dessus du header (z=50).
|
||||
Comme le panel n’empiète pas sur le header (top calculé), c’est safe.
|
||||
*/
|
||||
.page-panel{
|
||||
position: sticky;
|
||||
top: calc(var(--sticky-header-h) + var(--page-gap));
|
||||
align-self: start;
|
||||
color: var(--panel-text);
|
||||
|
||||
z-index: 80; /* ✅ au-dessus du header (50) => Lightbox non masquée */
|
||||
}
|
||||
|
||||
.page-panel__inner{
|
||||
max-height: calc(100vh - (var(--sticky-header-h) + var(--page-gap) + 12px));
|
||||
overflow: auto;
|
||||
scrollbar-gutter: stable;
|
||||
|
||||
border: 1px solid var(--panel-border);
|
||||
border-radius: var(--panel-radius);
|
||||
|
||||
padding: 14px;
|
||||
background:
|
||||
radial-gradient(1200px 600px at 20% 0%, rgba(140,140,255,0.10), transparent 55%),
|
||||
radial-gradient(900px 520px at 80% 20%, rgba(127,255,210,0.06), transparent 55%),
|
||||
linear-gradient(180deg, var(--panel-glass), transparent 68%);
|
||||
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
|
||||
box-shadow: var(--panel-shadow);
|
||||
|
||||
transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease), transform 140ms var(--ease);
|
||||
}
|
||||
|
||||
.page-panel.is-updating .page-panel__inner{
|
||||
border-color: var(--panel-border-strong);
|
||||
box-shadow: var(--panel-shadow-2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* ===== Head ===== */
|
||||
.panel-head{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
border-bottom: 1px solid rgba(127,127,127,0.18);
|
||||
}
|
||||
|
||||
.panel-head__left{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.panel-head__label{ font-weight: 900; opacity: .88; }
|
||||
|
||||
.panel-head__id{
|
||||
font-weight: 850;
|
||||
letter-spacing: .01em;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 16rem;
|
||||
|
||||
padding: 2px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--chip-bd);
|
||||
background: var(--chip-bg);
|
||||
}
|
||||
|
||||
/* ===== Messages ===== */
|
||||
.panel-msg{ margin-top: 8px; font-size: 12px; opacity: .92; }
|
||||
.panel-msg--head{ margin-top: 0; margin-bottom: 10px; }
|
||||
|
||||
/* ===== Toolbar actions : compacte + sticky ===== */
|
||||
.panel-top-actions{
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 6;
|
||||
|
||||
margin: 10px 0 10px;
|
||||
padding: 10px;
|
||||
|
||||
border: 1px solid rgba(127,127,127,0.14);
|
||||
border-radius: var(--panel-radius-sm);
|
||||
|
||||
background: rgba(255,255,255,0.55);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
|
||||
box-shadow: 0 10px 24px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-top-actions{
|
||||
background: rgba(0,0,0,0.28);
|
||||
box-shadow: 0 12px 28px rgba(0,0,0,0.22);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-top-actions::after{
|
||||
content: "";
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
height: 1px;
|
||||
background: rgba(127,127,127,0.16);
|
||||
}
|
||||
|
||||
/* ===== Buttons ===== */
|
||||
.panel-actions{
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel-btn{
|
||||
border: 1px solid var(--btn-bd);
|
||||
background: var(--btn-bg);
|
||||
border-radius: 999px;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
|
||||
transition: transform 120ms var(--ease2), background 120ms var(--ease2), border-color 120ms var(--ease2);
|
||||
}
|
||||
|
||||
.panel-btn:hover{
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--panel-border-strong);
|
||||
background: rgba(127,127,127,0.14);
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-btn:hover{ background: rgba(255,255,255,0.10); }
|
||||
}
|
||||
|
||||
.panel-btn:disabled{ opacity: .55; cursor: default; transform: none; }
|
||||
.panel-btn--primary{ font-weight: 900; border-color: var(--panel-border-strong); }
|
||||
|
||||
/* ===== Sections ===== */
|
||||
.panel-block{
|
||||
margin-top: 14px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid rgba(127,127,127,0.16);
|
||||
}
|
||||
.panel-block:first-of-type{ margin-top: 10px; border-top: 0; padding-top: 0; }
|
||||
|
||||
.panel-title{
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 950;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--panel-title);
|
||||
opacity: .96;
|
||||
}
|
||||
|
||||
.panel-subtitle{ font-size: 12px; font-weight: 900; margin: 12px 0 6px; opacity: .90; }
|
||||
.panel-body p{ margin: 8px 0; opacity: .92; }
|
||||
|
||||
.panel-list{ margin: 0; padding-left: 18px; }
|
||||
.panel-list li{ margin: 7px 0; }
|
||||
|
||||
.panel-chip{
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--chip-bd);
|
||||
background: var(--chip-bg);
|
||||
opacity: .95;
|
||||
}
|
||||
|
||||
.panel-quote{
|
||||
margin: 8px 0;
|
||||
padding: 10px 10px;
|
||||
border-left: 3px solid rgba(140,140,255,0.35);
|
||||
background: rgba(127,127,127,0.06);
|
||||
border-radius: 12px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-quote{ background: rgba(255,255,255,0.05); }
|
||||
}
|
||||
.panel-quote__src{ margin-top: 6px; font-size: 12px; opacity: .72; }
|
||||
|
||||
/* ===== Media grid ===== */
|
||||
.panel-media-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--thumb), 1fr));
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.panel-media-tile{
|
||||
width: 100%;
|
||||
border: 1px solid rgba(127,127,127,0.16);
|
||||
border-radius: 14px;
|
||||
padding: 8px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
transition: transform 120ms var(--ease2), background 120ms var(--ease2), border-color 120ms var(--ease2);
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-media-tile{ background: rgba(255,255,255,0.035); }
|
||||
}
|
||||
|
||||
.panel-media-tile:hover{
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--panel-border-strong);
|
||||
background: rgba(127,127,127,0.08);
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-media-tile:hover{ background: rgba(255,255,255,0.055); }
|
||||
}
|
||||
|
||||
.panel-media-tile img{
|
||||
width: 100%;
|
||||
height: var(--thumb);
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 8px;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.panel-media-ph{
|
||||
width: 100%;
|
||||
height: var(--thumb);
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: var(--chip-bg);
|
||||
margin-bottom: 8px;
|
||||
font-weight: 950;
|
||||
font-size: 12px;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
.panel-media-cap{
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
opacity: .92;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.panel-media-credit{
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
opacity: .74;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Featured (1 média) */
|
||||
.panel-media-grid > .panel-media-tile:only-child{
|
||||
grid-column: 1 / -1;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.panel-media-grid > .panel-media-tile:only-child img,
|
||||
.panel-media-grid > .panel-media-tile:only-child .panel-media-ph{
|
||||
height: min(52vh, 460px);
|
||||
object-fit: contain;
|
||||
background: rgba(0,0,0,0.08);
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
.panel-media-grid > .panel-media-tile:only-child img,
|
||||
.panel-media-grid > .panel-media-tile:only-child .panel-media-ph{
|
||||
background: rgba(0,0,0,0.22);
|
||||
}
|
||||
}
|
||||
.panel-media-grid > .panel-media-tile:only-child .panel-media-cap{
|
||||
white-space: normal;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
/* ===== Compose ===== */
|
||||
.panel-compose{
|
||||
margin-top: 12px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed rgba(127,127,127,0.22);
|
||||
}
|
||||
.panel-label{
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 950;
|
||||
opacity: .90;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.panel-textarea{
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(127,127,127,0.24);
|
||||
border-radius: 12px;
|
||||
padding: 9px 10px;
|
||||
background: transparent;
|
||||
font-size: 13px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
LIGHTBOX FIX (CRITIQUE)
|
||||
- overlay couvre le header
|
||||
- close toujours visible
|
||||
- le haut du média n’est jamais “mangé”
|
||||
- overrides forts (important) car styles inline du composant existent
|
||||
======================================================================= */
|
||||
|
||||
/* Quand hidden => pas d’interaction */
|
||||
.panel-lightbox[hidden]{ display: none !important; }
|
||||
|
||||
/* Conteneur full-viewport AU DESSUS de tout */
|
||||
.panel-lightbox{
|
||||
position: fixed !important;
|
||||
inset: 0 !important;
|
||||
z-index: 2147483647 !important; /* max practical */
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
/* Overlay : couvre TOUT y compris header */
|
||||
.panel-lightbox__overlay{
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
background: rgba(0,0,0,0.86) !important;
|
||||
backdrop-filter: blur(10px) !important;
|
||||
-webkit-backdrop-filter: blur(10px) !important;
|
||||
}
|
||||
|
||||
/* Dialog : on bannit transform/centering qui peut “couper” le haut.
|
||||
On utilise un inset fixe => le haut est toujours visible. */
|
||||
.panel-lightbox__dialog{
|
||||
position: fixed !important;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
right: auto !important;
|
||||
bottom: auto !important;
|
||||
transform: none !important;
|
||||
|
||||
inset: var(--lb-inset-top) var(--lb-inset-x) var(--lb-inset-bot) var(--lb-inset-x) !important;
|
||||
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
|
||||
border-radius: 18px !important;
|
||||
border: 1px solid rgba(255,255,255,0.14) !important;
|
||||
|
||||
background: rgba(18,18,18,0.58) !important;
|
||||
backdrop-filter: blur(14px) !important;
|
||||
-webkit-backdrop-filter: blur(14px) !important;
|
||||
|
||||
box-shadow: 0 28px 90px rgba(0,0,0,0.60) !important;
|
||||
overflow: hidden !important;
|
||||
|
||||
/* layout interne */
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
|
||||
/* petite anim (discrète) */
|
||||
animation: archiLbIn 160ms var(--ease) !important;
|
||||
}
|
||||
|
||||
@keyframes archiLbIn{
|
||||
from { opacity: 0; transform: translateY(-6px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Close : FIXED dans le viewport => ne peut plus être masqué par le header */
|
||||
.panel-lightbox__close{
|
||||
position: fixed !important;
|
||||
top: calc(var(--lb-inset-top) + 8px) !important;
|
||||
right: calc(var(--lb-inset-x) + 8px) !important;
|
||||
z-index: 2147483647 !important;
|
||||
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
|
||||
width: 46px !important;
|
||||
height: 42px !important;
|
||||
|
||||
border-radius: 14px !important;
|
||||
border: 1px solid rgba(255,255,255,0.30) !important;
|
||||
background: rgba(0,0,0,0.50) !important;
|
||||
color: rgba(255,255,255,0.92) !important;
|
||||
|
||||
cursor: pointer !important;
|
||||
font-size: 22px !important;
|
||||
font-weight: 950 !important;
|
||||
|
||||
transition: transform 120ms var(--ease2), background 120ms var(--ease2) !important;
|
||||
}
|
||||
.panel-lightbox__close:hover{
|
||||
transform: translateY(-1px) !important;
|
||||
background: rgba(255,255,255,0.14) !important;
|
||||
}
|
||||
|
||||
/* Contenu : prend l’espace, jamais sous le close (close est fixed viewport) */
|
||||
.panel-lightbox__content{
|
||||
flex: 1 1 auto !important;
|
||||
min-height: 0 !important;
|
||||
|
||||
padding: var(--lb-pad) !important;
|
||||
padding-top: 12px !important;
|
||||
|
||||
display: grid !important;
|
||||
place-items: center !important;
|
||||
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
/* Média : contain, et jamais coupé en haut (dialog inset fixe + scroll possible) */
|
||||
.panel-lightbox__content img,
|
||||
.panel-lightbox__content video{
|
||||
display: block !important;
|
||||
max-width: min(var(--lb-maxw), calc(100vw - (var(--lb-inset-x) * 2) - (var(--lb-pad) * 2))) !important;
|
||||
max-height: calc(100vh - var(--lb-inset-top) - var(--lb-inset-bot) - 64px) !important;
|
||||
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
|
||||
object-fit: contain !important;
|
||||
|
||||
background: rgba(0,0,0,0.22) !important;
|
||||
border-radius: 14px !important;
|
||||
}
|
||||
|
||||
/* audio */
|
||||
.panel-lightbox__content audio{
|
||||
width: min(860px, calc(100vw - (var(--lb-inset-x) * 2) - 28px)) !important;
|
||||
}
|
||||
|
||||
/* Caption : en bas, sans masquer le média (on reste simple) */
|
||||
.panel-lightbox__caption{
|
||||
position: relative !important;
|
||||
z-index: 2 !important;
|
||||
|
||||
padding: 10px 14px !important;
|
||||
font-size: 12px !important;
|
||||
font-weight: 950 !important;
|
||||
color: rgba(255,255,255,0.92) !important;
|
||||
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.72), rgba(0,0,0,0.00)) !important;
|
||||
}
|
||||
|
||||
/* Bonus : si le navigateur supporte :has(), on fige le scroll arrière-plan (premium) */
|
||||
@supports selector(html:has(*)){
|
||||
html:has(.panel-lightbox:not([hidden])){
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Responsive : comme avant, panel caché sous 1100px ===== */
|
||||
@media (max-width: 1100px){
|
||||
.page-panel{ display: none; }
|
||||
}
|
||||
|
||||
/* ===== Reduce motion ===== */
|
||||
@media (prefers-reduced-motion: reduce){
|
||||
.panel-lightbox__dialog{ animation: none !important; }
|
||||
.panel-btn,
|
||||
.panel-media-tile,
|
||||
.page-panel__inner{ transition: none !important; }
|
||||
}
|
||||
Reference in New Issue
Block a user