Files
portfolio-generator-app/CLAUDE.md
Valentin Le Moign 59dfbe4155 Générateur de portfolios PDF Figures Libres (app Nuxt autonome)
App Nuxt 3 + Nuxt UI qui lit les projets du CMS Grav et compose des portfolios
A3 paysage exportés en PDF (gabarit book_v3) : composeur WYSIWYG, templates
cover/toc/projet/grille/page libre, pipeline Playwright + Ghostscript, gel du
contenu à la génération, partage public opt-in.

Dépôt autonome : Dockerfile (base Playwright + Ghostscript + ffmpeg + rsync) inclus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 18:37:09 +02:00

68 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md — générateur de portfolios
App Nuxt (Nuxt 3 + Nuxt UI v3) qui lit les projets du CMS Grav de Figures Libres
et compose des portfolios **A3 paysage** exportés en PDF, fidèles au gabarit
`book_v3`. Ce dépôt est **autonome et déployable seul** (son `Dockerfile` est ici).
## Tout tourne dans Docker
Node, build Nuxt, Playwright/Chromium, Ghostscript, ffmpeg, rsync — **rien sur
l'hôte**. En dev, l'app est lancée via le dépôt d'orchestration `portfolio-stack`
(`docker compose up -d`), qui monte ce dossier en volume sur le port 8091.
```bash
# depuis le dépôt stack (ce dossier cloné en ./generator) :
docker compose run --rm --no-deps generator npm install # dépendances
docker compose exec generator npm run build # build de prod (.output)
docker logs -f figureslibres-portfolio-generator # logs du dev server
```
Mot de passe dev : `figureslibres` (`PORTFOLIO_PASSWORD`).
## Architecture
- `server/lib/` — le cœur :
- `content-source.ts` : abstrait la source de contenu. Mode `dev` = lecture
directe du FS Grav monté en `:ro` ; mode `prod` = cache alimenté par rsync/SSH.
- `grav-reader.ts` : parse les pages Grav (frontmatter YAML, `media_order`,
taxonomies) en `Project` (voir `types/index.ts`).
- `pdf-pipeline.ts` : rend `/print/<id>` via Playwright/Chromium en A3 paysage
(1190,55 × 841,89 pt), images à 2× (sharp), post-traitement Ghostscript, métadonnées.
- `portfolio-store.ts` : CRUD des portfolios (JSON sur disque), gel/dégel du contenu.
- `media.ts`, `session.ts`.
- `server/api/` — endpoints (auth, projects, portfolios, media, sync, exports, public).
- `components/templates/` — pages du gabarit (cover, toc, projet, grille, page libre).
- `components/preview/``PageRenderer` (dispatch) + `PageViewport` (aperçu à l'échelle).
- `components/composer/` — UI de composition (curation médias, fonds, pickers).
- `pages/print/[id].vue`**rendu print servant à la génération PDF** (ne pas casser).
- `types/index.ts` — modèle de données complet et commenté (source de vérité du schéma).
## Modèle de données (résumé)
Un `Portfolio` = `cover` (CoverConfig) + `pages[]`. Types de page : `cover`,
`toc`, `project` (couverture bandeau + image), `project-grid` (grille d'images
entières), `free`. Le contenu Grav (`Project`, `MediaAsset`) est **en lecture
seule**. À la génération, le contenu utilisé est **figé** dans `data/frozen/<id>/`
(`FrozenSnapshot`) : régénérer redonne le même PDF ; « Réactualiser depuis Grav »
resynchronise volontairement.
## Contraintes fermes
- Le contenu Grav n'est jamais modifié par l'app (monté `:ro` côté stack).
- Les images ne sont **jamais tronquées** dans les grilles (calées entières).
- Les vidéos ne sont pas sélectionnables dans les grilles.
- Format PDF : A3 paysage aux dimensions exactes du gabarit.
## Pièges Chromium print (avant de toucher aux templates)
- **`columns-*` (CSS multicol) = page PDF blanche** dans Chromium print. Ne pas
utiliser de colonnes CSS dans les templates ; vérifier chaque template via `pdftoppm`.
- **Webfonts en `font-display: block` = texte absent du PDF**. Le pipeline
réinvalide les styles avant `page.pdf()` — ne pas retirer cette étape.
## Données
Tout est sur disque dans `data/` (sauvegarde = copie du dossier), git-ignoré :
`portfolios/*.json`, `exports/*.pdf`, `frozen/<id>/`, `cache/` (régénérable),
`content-cache/` (rsync prod).