feat: page interactive, démos Play/Step et simulateur Carnot

Ajoute le pipeline PageSpec (validation, rendu, publication /p/{slug}),
les démos TipTap /demo, et le simulateur Carnot (modes frigo/PAC/moteur,
énergie kJ vs puissance W, unités K/°C/°F) avec correctifs d’équations KaTeX.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-07-24 17:51:43 +00:00
parent f385d43d5d
commit 69c99e4f4f
67 changed files with 12005 additions and 33 deletions

View File

@@ -0,0 +1,59 @@
# Simulateurs interactifs (plugins)
Bibliothèque de simulateurs pédagogiques pour les **pages interactives** (`/p/{slug}`).
L'IA ne code jamais une simulation : elle **choisit** un simulateur de cette liste et le
**configure** (`preset` = valeurs de la note), ou utilise `generic-formula` si aucun ne
correspond au sujet.
## Liste des simulateurs disponibles
| `simId` | Famille | Sujet | Interaction |
|---|---|---|---|
| `carnot-cycle` | `sim` | Machine frigorifique / PAC / moteur de Carnot (2ᵉ principe) | Curseurs T_c, T_h, Q_c → COP, η, W min, Q_h en direct |
| `carnot-cycle-anim` | `anim` | Cycle de Carnot animé (piston + diagramme PV live, 5 étapes) | Play / Pause / Step / Reset / vitesse + narration |
| `generic-formula` | `sim` | (générique) toute relation chiffrée y = f(paramètres) | Curseurs définis par l'IA, expressions sûres (aucun `eval`) |
Deux familles de plugins : `sim` (manipulation de paramètres, calcul en direct) et
`anim` (scène animée codée en dur, pilotée par le player Play/Step — narration par
battements). Les deux sont choisis par l'IA via le même bloc `{ "type": "sim" }`.
## Comment l'IA les utilise
1. Le prompt de génération de section injecte `catalogForPrompt(lang)` (registry.ts) —
id + résumé + mots-clés + bornes de chaque simulateur.
2. Si le contenu de la note correspond (`keywords`), l'IA émet
`{ "type": "sim", "sim": { "simId": "carnot-cycle", "preset": { "t_hot": 300 } } }`.
3. `validateInteractivePage` vérifie : `simId` connu, preset dans les bornes,
`compute(preset)` fini — sinon rejet (renvoyé au LLM ou fallback).
## Ajouter un plugin (5 étapes)
Pour un **`sim`** (curseurs) :
1. **`lib/simulators/<id>.ts`** — implémenter `SimulatorPlugin` (`family: 'sim'`) :
`id`, `title`/`summary` fr+en (le summary sert au matching IA), `keywords`,
`params` (curseurs : bornes, pas, défaut, unité, intent), `outputs` (symbole KaTeX,
unité), et `compute(env)` **pur et déterministe**.
2. **`lib/simulators/index.ts`** — ajouter au `REGISTRY`.
3. **`components/simulators/<id>-view.tsx`** — composant sur mesure
(props : `preset`, `title`, `disclaimer`, `lang`). Réutiliser
`SimSlider` / `SimOutputCard` / `SimHeading` / `SimKaTeX` de `sim-controls.tsx`.
4. **`components/simulators/index.ts`** — enregistrer dans `SIMULATOR_VIEWS`.
5. Vérifier : `compute` fini aux valeurs par défaut, `npx tsc --noEmit`, tester sur
`/dev/interactive-page`.
Pour une **`anim`** (scène animée Play/Step) :
1. **`lib/simulators/<id>.ts`** — implémenter `AnimPlugin` (`family: 'anim'`) :
`id`, `title`/`summary` fr+en, `keywords`, `disclaimer?`, `beats` (narration
fr+en par étape — KaTeX inline `$…$` dans `speak`).
2. **`lib/simulators/index.ts`** — ajouter au `REGISTRY`.
3. **`components/simulators/<id>-anim-view.tsx`** — scène SVG/React pilotée par
`step` (props : `step`, `lang`). Transitions CSS sur `transform`/`opacity`
uniquement. Le chrome (Play/Pause/Step/Reset/vitesse + panneau de narration +
clavier Espace/←/→/R) est fourni par `AnimPlayerShell` — ne pas le réécrire.
4. **`components/simulators/index.ts`** — enregistrer dans `ANIM_VIEWS`.
5. Vérifier : chaque `step` rend un état cohérent (y compris état final sans JS),
`npx tsc --noEmit`, tester sur `/dev/interactive-page`.
Règles : pas de couleurs en dur (intents / tokens `--pp-*`), labels fr+en dans le
plugin, tout calcul côté `compute` ou scène codée (jamais de logique LLM), SSR =
état lisible sans JS.