Files
portfolio-generator-stack/docs/spec-generateur-portfolios.md
Valentin Le Moign 1069bba2e4 docs: versionner spec + guide de déploiement (hors portfolios-examples)
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.
2026-07-06 13:48:31 +02:00

29 KiB
Raw Permalink Blame History

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 :

  1. Se connecter (mot de passe partagé).
  2. (Optionnel) Cliquer « Rafraîchir » pour re-synchroniser le contenu depuis la prod.
  3. Créer un nouveau portfolio (ou dupliquer un existant).
  4. Parcourir la bibliothèque de projets, filtrer par catégorie/tag, cocher ceux à inclure.
  5. Pour chaque page : choisir le template, curer les médias, ajuster le texte si besoin.
  6. Insérer des pages libres (couverture, intro, sommaire) et les personnaliser.
  7. Réordonner les pages (glisser-déposer).
  8. Prévisualiser (rendu WYSIWYG identique au PDF).
  9. Générer le PDF, renseigner le nom de fichier, télécharger.
  10. 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 ssh tirant user/pages/02.projets/ (Markdown + médias) de la prod vers data/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 frozenContent dans 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-face pour 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 (≈ 150200 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)

  1. 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.
  2. 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.

  1. 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/@4x de Grav servent de source haute résolution ; on descend vers la taille utile plutôt que d'embarquer des images surdimensionnées.
  2. 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).
  3. Impression : Playwright/Chromium page.pdf() en mode print, format 16:9, printBackground: true.
  4. 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).
  5. Sortie : data/exports/<nom-fichier>.pdf, proposé au téléchargement.

Les vidéos .mp4 sont 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.
  • ContentSource mode 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-text et grid + 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

  • ContentSource mode 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

  1. 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.
  2. Sommaire : numérotation de pages souhaitée ? niveau de détail (par projet, par section) ?
  3. 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).
  4. Sous-titre projet : dériver automatiquement de taxonomy.category, ou champ dédié éditable dans l'UI ? (défaut proposé : dérivé + override.)
  5. Accès prod : confirmer l'accès SSH (et non seulement FTP) pour rsync, + le chemin exact de user/pages sur 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 + 24 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/npx s'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 sudo sans 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 en preview et print.
  • 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 23 ; ne pas bloquer les phases 01 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.