Guide de déploiement prod : VM Debian nue (sans Docker), service systemd, déploiement par push Git (bare repo + post-receive), branchement rsync/SSH en lecture seule à la prod Grav, reverse proxy HTTPS, dimensionnement VM.
29 KiB
Spécification technique — Générateur de portfolios PDF (Figures Libres)
Document de spécification v1 — établi le 2026-07-01, révisé le 2026-07-02. Outil compagnon du site Grav « Figures Libres » permettant de composer et générer des portfolios PDF à partir des projets saisis dans Grav.
1. Contexte & objectif
Le collectif Figures Libres gère ses projets dans un site Grav (v1.7.52). Chaque projet y est déjà décrit (titre, texte, médias, catégorie, tags, date, lien). L'objectif est de réutiliser ces contenus pour produire des portfolios PDF sur mesure, sans ressaisie :
- sélectionner/désélectionner les projets à inclure ;
- ordonner les pages ;
- choisir, pour chaque page, quel projet est présenté avec quel template ;
- curer les médias affichés page par page (choix + ordre + disposition) ;
- intercaler des pages libres (couverture, intro, sommaire) personnalisables ;
- exporter un PDF léger et net, destiné à l'envoi par mail.
L'outil est séparé de Grav (pas un plugin) : une application web dédiée qui lit le contenu Grav en lecture seule.
2. Décisions validées (récapitulatif des arbitrages)
| Sujet | Décision |
|---|---|
| Utilisateurs | Interne au collectif (quelques utilisateurs de confiance) |
| Qualité PDF | Écran / partage (RGB, pas de contrainte imprimeur) |
| Templates | Définis en code (pas d'éditeur visuel de template) |
| Composition | 1 projet / page + curation des médias (sélection + ordre + layout, sans recadrage) |
| Sauvegarde | Oui : bibliothèque de portfolios rééditables |
| Pages non-projet | Couverture, sommaire, pages libres d'intro (personnalisables dans l'UI) ; séparateurs/4e de couv optionnels |
| Texte des pages | Auto depuis Grav + override ponctuel par page |
| Format de page | Fixe, 16:9 « écran » paysage (voir §9.4) |
| Fraîcheur des données | Live + figé à la génération (le portfolio archive le contenu utilisé) |
| Sections source | Projets uniquement (user/pages/02.projets) |
| Champs exploités | Titre, texte, médias, catégorie/tags, date, lien externe |
| Médias | UI en basse résolution ; génération en pleine résolution ; post-traitement du PDF pour redimensionner chaque image à sa taille d'affichage |
| Personnalisation à la génération | Titre/sous-titre couverture, nom du fichier PDF ; couverture + pages libres personnalisables |
| Langue | FR uniquement |
| Fonds de page | Blanc / teinté / pleine couleur (prop background), dès la v1 |
| Direction artistique | Sobre : pas de formes graphiques décoratives ni d'effets « fluo » |
| Stack | Nuxt 3 + Nuxt UI + Playwright (Chromium) + Paged.js |
| Auth | Mot de passe partagé simple |
| Stockage | Fichiers JSON + PDF sur disque (volume Docker) |
| Déploiement | Serveur perso (Docker + Playwright), contenu tiré de la prod en rsync/SSH lecture seule |
| Synchro | Bouton manuel « Rafraîchir » |
| Templates de départ | « Visuel dominant + texte » et « Grille / planche » ; layouts d'images variables (§9.5) |
| Références d'inspiration | 2 books dans docs/portfolios-examples/ — inspiration non-normative (§17) |
3. Utilisateurs & cas d'usage
Utilisateur type : un membre du collectif préparant un portfolio pour une candidature, un appel d'offres ou un envoi client.
Parcours principal :
- Se connecter (mot de passe partagé).
- (Optionnel) Cliquer « Rafraîchir » pour re-synchroniser le contenu depuis la prod.
- Créer un nouveau portfolio (ou dupliquer un existant).
- Parcourir la bibliothèque de projets, filtrer par catégorie/tag, cocher ceux à inclure.
- Pour chaque page : choisir le template, curer les médias, ajuster le texte si besoin.
- Insérer des pages libres (couverture, intro, sommaire) et les personnaliser.
- Réordonner les pages (glisser-déposer).
- Prévisualiser (rendu WYSIWYG identique au PDF).
- Générer le PDF, renseigner le nom de fichier, télécharger.
- Le portfolio reste sauvegardé et rééditable.
4. Architecture générale
┌──────────────────────────────────────────────────────────────┐
│ Serveur perso (Docker) │
│ │
│ ┌────────────────────┐ ┌──────────────────────────────┐ │
│ │ Nuxt 3 (UI + API) │ │ Cache de contenu (lecture) │ │
│ │ - Bibliothèque │◀────▶│ user/pages/02.projets/… │ │
│ │ - Composeur │ │ (md + médias) │ │
│ │ - Prévisualisation│ └──────────────▲───────────────┘ │
│ │ - Templates (Vue) │ │ rsync/SSH (RO) │
│ └─────────┬──────────┘ │ (bouton │
│ │ rendu HTML │ « Rafraîchir ») │
│ ▼ │ │
│ ┌────────────────────┐ ┌──────────────┴───────────────┐ │
│ │ Playwright/Chromium│ │ Stockage portfolios │ │
│ │ + Paged.js │ │ data/portfolios/*.json │ │
│ │ → PDF │─────▶│ data/exports/*.pdf │ │
│ │ + sharp + gs (opti)│ └──────────────────────────────┘ │
│ └────────────────────┘ │
└───────────────────────────────────┬──────────────────────────┘
│ (SSH lecture seule)
┌──────────▼───────────┐
│ Serveur PROD Grav │
│ figureslibres.cc │
│ (aucune charge PDF) │
└───────────────────────┘
Principe clé : la prod ne fait que servir le site et exposer son contenu en SSH. Toute la charge lourde (Playwright, génération, optimisation PDF) tourne sur le serveur perso.
5. Topologie de déploiement & sources de contenu
L'outil supporte deux sources de contenu via une même interface d'abstraction
(ContentSource), sélectionnée par configuration :
5.1 Mode DEV (immédiat, sans prod)
- Source = le Grav local présent dans ce dépôt (
../grav-reference/user/pages/02.projets). - Lecture directe du système de fichiers (le dossier est déjà monté sur le serveur).
- Permet de développer et tester tout l'outil dès maintenant, avant tout accès prod.
5.2 Mode PROD (cible)
- Source = cache local alimenté par rsync/SSH depuis la prod Figures Libres.
- L'outil ne se connecte jamais en écriture à la prod.
Le passage dev → prod ne change que la configuration de la source, pas le modèle de données ni les templates.
6. Synchronisation du contenu (mode PROD)
- Mécanisme :
rsync -az --delete -e sshtirantuser/pages/02.projets/(Markdown + médias) de la prod versdata/content-cache/. - Déclenchement : bouton manuel « Rafraîchir » dans l'UI (endpoint API qui lance la commande et renvoie un résumé : nb de fichiers mis à jour, date).
- Incrémental : rsync ne transfère que les fichiers modifiés → rapide même avec de nombreux médias lourds.
- Lecture seule : aucune écriture côté prod ; identifiants SSH stockés en secret (variable d'environnement / fichier non versionné).
- Note git : le contenu (
user/pages/*) est exclu de git côté Grav — rsync est donc le bon outil (git ne verrait pas les projets ni les médias).
7. Modèle de données
7.1 Lecture du contenu Grav
Structure observée dans le Grav Figures Libres :
user/pages/02.projets/
01.culturelle/ ← catégorie (dossier)
atlas-atterrissage/ ← projet (dossier, slug)
reader.md ← frontmatter YAML + corps Markdown
*.png / *.mp4 ← médias (+ variantes @2x/@3x/@4x)
02.en-francais-au-pluriel/
reader.md
…
02.publique/
03.sociale/
Frontmatter type d'un projet :
title: 'En français au pluriel'
aura:
image: Screenshot_….png # visuel de couverture du projet
media_order: 'a.png,b.png,…' # ordre des médias
taxonomy:
category: ['Site web', 'Identité visuelle']
tag: ['Collectif Rivage']
date: '09-12-2024 22:19'
published: true
- corps Markdown (description, éventuel lien externe en fin de texte).
7.2 Modèle interne (TypeScript, indicatif)
// --- Contenu importé de Grav (lecture seule) ---
interface Project {
id: string; // slug complet, ex: "02.projets/01.culturelle/atlas-atterrissage"
category: string; // "culturelle" | "publique" | "sociale"
title: string;
bodyMarkdown: string; // texte descriptif
coverImage?: string; // aura.image
media: MediaAsset[]; // dans l'ordre de media_order
categories: string[]; // taxonomy.category
tags: string[]; // taxonomy.tag
date?: string; // ISO
externalUrl?: string; // lien détecté dans le corps
published: boolean;
}
interface MediaAsset {
filename: string;
type: 'image' | 'video';
variants: { label: '1x'|'2x'|'3x'|'4x'; path: string; width?: number }[];
posterPath?: string; // pour les vidéos : image d'aperçu
}
// --- Composition (créée dans l'outil, sauvegardée) ---
interface Portfolio {
id: string;
name: string; // nom interne
cover: CoverConfig; // titre/sous-titre/… personnalisables
pages: Page[]; // ordre = ordre du PDF
createdAt: string;
updatedAt: string;
frozenContent?: FrozenSnapshot; // rempli à la génération (voir §7.3)
}
type Page =
| ProjectPage
| FreePage
| CoverPage
| TocPage;
// Fond de page commun à tous les types (voir §9.5)
type Background =
| { mode: 'white' }
| { mode: 'tint'; color: string } // aplat clair
| { mode: 'color'; color: string }; // pleine couleur
interface ProjectPage {
kind: 'project';
projectId: string;
template: 'hero-text' | 'grid'; // templates définis en code
layout: string; // disposition d'images (single|mosaic|grid-2|…), voir §9.5
mediaSelection: string[]; // filenames choisis + ordonnés (curation, sans recadrage)
subtitleOverride?: string; // sinon dérivé de taxonomy.category
textOverride?: string; // override ponctuel du texte (sinon = Grav)
background: Background;
options?: Record<string, unknown>; // réglages mineurs éventuels du template
}
interface FreePage { // page libre d'intro / texte
kind: 'free';
template: 'free-text';
title?: string;
body?: string; // markdown saisi dans l'UI
media?: string[]; // médias optionnels
background: Background;
}
interface CoverConfig { // page de couverture
title: string;
subtitle?: string;
recipient?: string; // « préparé pour … »
backgroundImage?: string;
background: Background;
}
interface TocPage { kind: 'toc'; title?: string; background: Background; }
7.3 « Live + figé à la génération »
- Pendant l'édition, l'outil lit le contenu live (cache synchronisé) → les projets reflètent l'état courant de Grav.
- À la génération du PDF, l'outil écrit un
frozenContentdans le portfolio sauvegardé : copie des textes, listes de médias et fichiers réellement utilisés. - Conséquence : régénérer un ancien portfolio produit le même PDF, même si Grav a changé depuis. Un bouton « Réactualiser depuis Grav » permettra de re-synchroniser volontairement un portfolio avec le contenu à jour.
8. Fonctionnalités détaillées
8.1 Bibliothèque de projets
- Liste des projets par catégorie, avec vignette (basse résolution), titre, tags, date.
- Recherche + filtres (catégorie, tag).
- Sélection/désélection pour ajout au portfolio.
8.2 Composeur de portfolio
- Colonne « pages du portfolio » (liste réordonnable en glisser-déposer).
- Ajout de pages : depuis un projet (choix du template) ou page spéciale (couverture, sommaire, page libre).
- Panneau d'édition de la page sélectionnée :
- choix du template (parmi ceux définis en code) ;
- curation des médias : cocher/décocher + réordonner les visuels du projet (aperçus basse résolution), sans recadrage/zoom ;
- disposition (layout) : choix parmi les layouts du template (§9.5) ;
- texte : pré-rempli depuis Grav, éditable (override local, sans toucher Grav) ;
- fond de page : blanc / teinté / pleine couleur (
background) ; - réglages mineurs du template le cas échéant.
8.3 Pages spéciales / « pages types »
- Couverture : titre, sous-titre, destinataire, image de fond — personnalisables.
- Sommaire : généré automatiquement depuis les pages-projet (titres + n° de page).
- Page libre d'intro : titre + texte Markdown + médias optionnels, saisis dans l'UI.
- (Optionnel v1+) séparateurs de section, 4e de couverture.
- Ces pages sont elles aussi des templates définis en code, personnalisables via l'UI.
8.4 Prévisualisation WYSIWYG
- Rendu à l'écran par les mêmes composants Vue que ceux utilisés pour le PDF.
- Ce que l'on voit = ce que produira le PDF (mêmes styles, même pagination logique).
- En prévisualisation, les images sont servies en basse résolution (fluidité).
8.5 Génération PDF
- Nom de fichier personnalisable ; métadonnées PDF (titre/auteur) renseignées.
- Pipeline détaillé au §10.
8.6 Gestion des portfolios sauvegardés
- Liste des portfolios (nom, date de MAJ, aperçu couverture).
- Actions : ouvrir, dupliquer, renommer, supprimer, régénérer le PDF.
9. Système de templates
9.1 Principe
- Templates = composants Vue dans
generator/components/templates/. - Un même composant sert aperçu écran et rendu PDF (source de vérité unique).
- Contrat d'entrée commun (props) pour être interchangeables dans le composeur.
9.2 Contrat d'interface (props)
interface TemplateProps {
project?: Project; // pour les pages-projet
subtitle?: string; // sous-titre "type de travail" (dérivé de taxonomy.category)
media: ResolvedMedia[]; // médias curés, déjà résolus à la bonne résolution
layout?: string; // disposition d'images choisie (voir §9.5)
text?: string; // texte effectif (override ou Grav)
background: Background; // fond de page (blanc / teinté / pleine couleur) — voir §7.2
page: { index: number; total: number };
theme: FiguresLibresTheme; // charte (couleurs, fonts) — voir §9.3
renderMode: 'preview' | 'print';
}
Sous-titre projet : dans les books existants, chaque page-projet porte un sous-titre « type de travail » (ex. « Identité visuelle et rapports d'activités », « Édition / principe de la collection »). Il sera dérivé de
taxonomy.category(joignables) ou éditable en override.
9.3 Charte Figures Libres (extraite du thème Grav figureslibres-v2)
- Couleurs (extraites du thème) : base claire
#f5f5f5, encre#0e1229(bleu nuit). Couleurs secondaires disponibles :#ffaeab,#feff74,#71ff94,#fabbde,#4bffc9, à employer avec parcimonie (aplats de fond, filets). Direction artistique sobre : pas de formes graphiques décoratives ni d'effets « fluo ». - Polices maison disponibles (réutilisables, fichiers woff2 présents dans le thème) : Avara (serif de titre), Moche, Syne, ManifontGrotesk, PlayfairDisplay, Rumeur, Lato.
- Les fonts seront embarquées dans l'app via
@font-facepour un rendu identique en PDF.
9.4 Format de page (à valider)
- Format « écran » 16:9 paysage.
- Recommandation : canvas de référence 1280×720 pt (ratio 16:9), avec les images redimensionnées à 2× leur boîte d'affichage (≈ 150–200 dpi effectif) → netteté sans surpoids. Le PDF garde du texte vectoriel (léger et net à tout zoom).
@page { size: 1280pt 720pt; margin: 0 }côté CSS Paged.js (valeurs ajustables).- 1920×1080 px reste la cible d'affichage logique ; la valeur pt ci-dessus en est l'équivalent physique pratique pour le PDF.
9.5 Templates de départ (v1)
hero-text— Visuel dominant + texte : un grand visuel + bloc titre/sous-titre/ description (date, catégorie/tags, lien externe en pied). Le template « projet » de référence.grid— Grille / planche : plusieurs médias d'un même projet en grille, titre discret.
Dispositions d'images (layouts). L'analyse des books existants (voir §17) montre que la page-projet réclame des compositions variables, pas une grille unique. La curation médias inclut donc le choix d'une disposition parmi un jeu prédéfini, ex. :
single: une image plein cadre (bord à bord ou avec marge) ;mosaic: mosaïque asymétrique (1 grande + plusieurs petites) ;grid-2/grid-3/grid-6: grilles régulières ;image-text: une image + un bloc de texte latéral.
Ces layouts sont des variantes internes aux templates (props layout + media), pas des
templates séparés — ils partagent l'en-tête (titre + sous-titre) et la charte.
Les deux books de référence sont désormais dans
docs/portfolios-examples/. Ce sont des inspirations libres, pas des gabarits à reproduire : ils sont en A3/A4 paysage (impression), alors que la cible est le 16:9 écran. On s'en sert pour le vocabulaire de pages (§17) et l'esprit graphique, pas pour un pixel-perfect.
10. Pipeline PDF & optimisation des images
Objectif : PDF léger (envoyable par mail) ET images irréprochables.
- Résolution des médias : pour chaque image d'une page, calcul de sa taille
d'affichage réelle dans le template au format 16:9, puis génération (via
sharp) d'une version redimensionnée àtaille_affichage × 2(retina). Les variantes@2x/@3x/@4xde Grav servent de source haute résolution ; on descend vers la taille utile plutôt que d'embarquer des images surdimensionnées. - Rendu HTML : Nuxt rend la page composée (mêmes composants que l'aperçu), Paged.js gère la pagination (une page CSS = une page PDF).
- Impression : Playwright/Chromium
page.pdf()en modeprint, format 16:9,printBackground: true. - Post-traitement : passe Ghostscript (
gs) pour recompresser/normaliser (downsampling au DPI cible, compression JPEG maîtrisée) et alléger le fichier final. Métadonnées PDF injectées (titre, auteur). - Sortie :
data/exports/<nom-fichier>.pdf, proposé au téléchargement.
Les vidéos
.mp4sont représentées par un poster (image d'aperçu) puisqu'un PDF est statique.
11. Stack technique & arborescence
Principe d'infrastructure : tout est conteneurisé. Aucun runtime (Node, etc.) ni binaire système n'est installé sur l'hôte, qui héberge déjà de nombreux services. Node, le build, Playwright/Chromium et Ghostscript s'exécutent dans des conteneurs Docker.
- Front + API : Nuxt 3 (Nitro pour les endpoints serveur), Nuxt UI pour les composants.
- PDF : Playwright (Chromium) + Paged.js.
- Images : sharp ; Ghostscript (dans le conteneur) pour l'optimisation PDF.
- Parsing Grav : lecture FS + parseur front-matter YAML + Markdown.
- Synchro : rsync/ssh (dans le conteneur), déclenché par un endpoint Nitro.
Arborescence cible (dans ce dépôt, à côté de grav-reference/ = Grav) :
portfolio-maker/
grav-reference/ ← Grav de référence (existant, renommé depuis app/)
generator/ ← nouvelle app Nuxt (code)
components/
templates/ ← hero-text.vue, grid.vue, cover.vue, toc.vue, free-text.vue
composer/ ← UI de composition
server/
api/ ← endpoints (projects, portfolios, sync, generate)
lib/ ← grav-reader, media-resolver, pdf-pipeline, content-source
data/
content-cache/ ← contenu synchronisé (prod) — non versionné
portfolios/*.json ← compositions sauvegardées
exports/*.pdf ← PDF générés
nuxt.config.ts
docker/
grav/Dockerfile ← (existant) image du Grav de référence
generator/Dockerfile ← image du générateur (base image Playwright + Ghostscript)
docker-compose.yml ← services grav-reference + generator
docs/
spec-generateur-portfolios.md ← ce document
12. Sécurité & accès
- Mot de passe partagé unique (middleware d'auth simple + cookie de session).
- Accessible sur le LAN comme Grav (port dédié, ex. 3000/8090).
- Identifiants SSH de la prod stockés hors dépôt (env/secret), utilisés uniquement pour rsync en lecture.
13. Stockage
- Portfolios : un fichier JSON par portfolio (
data/portfolios/<id>.json). - Exports : PDF dans
data/exports/. - Cache contenu :
data/content-cache/(rsync). - Tout dans un volume Docker → sauvegardable par simple copie de dossier.
14. Roadmap par phases
Phase 0 — Fondations (dev sur Grav local)
- App Nuxt + Nuxt UI, auth mot de passe.
ContentSourcemode DEV : lecture de../grav-reference/user/pages/02.projets.- Parseur Grav (projets, médias, taxonomie, lien).
- Bibliothèque de projets (liste, filtres).
Phase 1 — Composition & aperçu
- Modèle Portfolio + sauvegarde JSON.
- Composeur : ajout/réordonnancement de pages, choix template, curation médias, override texte.
- Templates
hero-textetgrid+ charte Figures Libres. - Prévisualisation WYSIWYG.
Phase 2 — Génération PDF
- Pipeline Playwright + Paged.js au format 16:9.
- Optimisation images (sharp) + post-traitement Ghostscript.
- Pages spéciales : couverture, sommaire, page libre.
- Personnalisation (titre/sous-titre, nom de fichier, métadonnées).
- Gel du contenu à la génération (
frozenContent).
Phase 3 — Passage prod & finitions
ContentSourcemode PROD : rsync/SSH + bouton « Rafraîchir ».- Conteneur Docker + intégration au
docker-compose. - Raffinement des templates à partir des PDF d'exemple.
- Gestion de bibliothèque (dupliquer, renommer, supprimer, régénérer).
15. Décisions ouvertes / à confirmer
- Format de page : valider
1280×720 pt(16:9) et le facteur retina ×2 pour les images (§9.4), ou fournir des dimensions précises attendues. - Sommaire : numérotation de pages souhaitée ? niveau de détail (par projet, par section) ?
- Plusieurs pages par projet : les books montrent souvent 1 projet = 1 page, mais parfois un projet riche s'étale. Confirmer si on autorise plusieurs pages pour un même projet (actuellement : non, 1 projet = 1 page).
- Sous-titre projet : dériver automatiquement de
taxonomy.category, ou champ dédié éditable dans l'UI ? (défaut proposé : dérivé + override.) - Accès prod : confirmer l'accès SSH (et non seulement FTP) pour rsync, + le
chemin exact de
user/pagessur la prod.
Décidé : fonds de page teintés/pleine couleur proposés dès la v1 (prop background
= white | tint | color). Curation médias = sélection + ordre + choix de layout,
sans recadrage/zoom d'image en v1._
16. Annexe — Rappel de la structure de contenu observée
- Projets sous
user/pages/02.projets/{01.culturelle,02.publique,03.sociale}/<slug>/reader.md. - Médias à côté du
.md, variantes@2x/@3x/@4x, quelques.mp4. - Frontmatter clés utiles :
title,aura.image,media_order,taxonomy.category,taxonomy.tag,date,published. - Le corps Markdown contient la description et, souvent, l'URL externe du projet.
17. Annexe — Références d'inspiration (books existants)
⚠️ Non-normatif. Les deux PDF de
docs/portfolios-examples/(book_2025_Bagneux.pdf, 17 p. A4 paysage ;book2025_FiguresLibres_edition.pdf, 16 p. A3 paysage) sont des books faits main, fournis comme inspiration libre. Ils donnent l'esprit et le vocabulaire de pages attendus — pas des gabarits à reproduire au pixel. La cible reste le 16:9 écran.
Taxonomie de pages observée (à couvrir par des "pages types")
| Type de page | Description observée |
|---|---|
| Couverture | Date + titre du dossier (client) + coordonnées collectif + grande image. Personnalisable. |
| Présentation | Grand titre (« 20 ans d'expérience… ») + colonnes de texte + image latérale pleine hauteur + logo en pastille. |
| Équipe | Grille 2 colonnes × N : portrait rond + nom/rôle en gras + bio. Peut couvrir 2 pages. |
| Colonnes / infos | Titre + 2–4 colonnes de texte à sous-titres, filet vertical coloré (« Outils et savoir-faire », « Commanditaires »). |
| Intercalaire-titre | Grand titre sur page quasi vide (« Portfolio Figures Libres »). |
| Page-projet | En-tête (titre + sous-titre "type de travail") + composition d'images variable (voir layouts §9.5) ± bloc texte ± fond teinté. |
| 4e de couverture | Contact « Figures Libres.cc » + adresse + citation d'un membre. |
Grammaire graphique retenue comme inspiration
- Logo en pastille (cercle noir ou couleur).
- Titres grotesques géométriques arrondis (Syne / Moche) ; texte courant en Lato.
- Fonds blancs, gris très clairs, beige, ou pleine couleur selon la page/projet.
- En-tête projet titre + sous-titre systématique ; pagination + mention « FIGURES LIBRES » en pied.
- Compositions d'images asymétriques et respirées (une dominante + satellites), pas seulement des grilles régulières.
18. Notes pour le développement autonome
Par où commencer. Démarrer par la Phase 0 sur le Grav local. Le CMS de référence est
déjà servi (docker compose up -d, http://localhost:8089) et son contenu est lisible dans
../grav-reference/user/pages/02.projets/. Aucune connexion prod n'est nécessaire pour les
phases 0 à 2.
Contraintes fermes.
- Ne jamais écrire dans
grav-reference/: c'est une source en lecture seule. - TOUT tourne dans des conteneurs Docker — rien n'est installé sur l'hôte. Le serveur
héberge de nombreux services ; on n'y ajoute aucun runtime ni paquet. Node, npm/pnpm, le
build Nuxt, Playwright/Chromium et Ghostscript vivent dans des conteneurs. Toute
commande
node/npm/npxs'exécute via un conteneur (ex.docker compose run), jamais sur la machine. - Base recommandée : image officielle Playwright (
mcr.microsoft.com/playwright, Node + Chromium + dépendances déjà présents), + Ghostscript ajouté dans l'image du service. - Ne rien installer sur le serveur ni lancer de
sudosans validation explicite de l'utilisateur. - FR uniquement. DA sobre : pas de formes graphiques décoratives, pas d'effets « fluo ».
Conventions.
- Un template = un composant Vue piloté uniquement par ses props (
TemplateProps, §9.2) : aucun accès direct au système de fichiers depuis un template → testable et identique enpreviewetprint. - Toute lecture de contenu passe par l'abstraction
ContentSource(§5) : brancher DEV d'abord, PROD ensuite sans toucher au reste. - Ghostscript et rsync (dans le conteneur) ne servent qu'aux phases 2–3 ; ne pas bloquer les phases 0–1 dessus.
Outils disponibles pour l'agent.
- context7 (MCP branché) : doc à jour des libs (Nuxt 3, Nuxt UI, Playwright, Paged.js) — s'en servir plutôt que deviner les APIs.
- poppler-utils : lire les PDF générés pour auto-vérifier le rendu et le comparer aux books d'inspiration (§17).
Definition of done — Phase 0. La bibliothèque liste les projets réels du
grav-reference (les 3 catégories), avec vignette basse résolution, titre, tags et date,
filtrables ; l'auth par mot de passe protège l'accès ; rien n'est écrit côté Grav.