05 — Le format motion : la vidéo avec emaki
Statut : fait · Phase : P4 · Mis à jour : 25/10/2026.
Source : the emaki repository. Ce document reprend emaki/PLAN.md et répond à ses questions
ouvertes, puisque le projet hôte est maintenant Récit.
0. Ce qu'apporte emaki
-
10 packages
@emaki/*en0.1.0-beta.7(studioest privé) :Package Rôle schemaDeckzod 4,parseDeck(seule source de vérité), opscoremoteur et timing uiprimitives blockstitle,chapter,statement,stat,list,compare-bars,ui-mock,ui-scene(framer-motion 12)themeswarm-editorial,saas-productrenderRemotion 4 vers MP4 extractle handover, format souple sans idmcpserveur MCP clicommande emakistudioéditeur Monaco -
Formats d'image :
16:9,1:1,9:16. -
Garde-fous : goldens PNG (
fixtures/goldens/saas-*.pngdans emaki, aujourd'huipackages/formats/motion-render/fixtures/goldens/,pnpm -F @recit/motion-render goldens:check), tests vitest et règle « aucune métrique inventée ». -
« Deux chemins de rendu qui correspondent » : le même moteur de blocs joue dans le navigateur (prévisualisation, studio) et dans Remotion (MP4). C'est ce qui permettra d'intégrer une vidéo jouable dans une page du framework de sites sans Remotion.
1. Réponses aux questions ouvertes de emaki/PLAN.md
| Question | Réponse |
|---|---|
| Projet hôte ? | Récit (ce fork) : pnpm 11, Node ≥ 22.22, nx, React 19.2, zod 4.6, TS 6, Vite 8 |
| Mode d'intégration (phase 1) | Copie, comme presentation-engine (04-migration.md) : le .git local d'emaki a été supprimé. Référence : dernier commit 1b86ccd (0.1.0-beta.7), cité dans chaque commit d'import |
| Emplacement et scope | copie directe dans les dossiers finaux (packages/formats/motion*, packages/mcp), sans dossier emaki/ intermédiaire. Le scope devient @recit/* (voir le tableau de 03-packaging.md) |
| Emaki reste-t-il publiable seul ? | Non : les packages deviennent @recit/motion*. Les @emaki/*@beta déjà publiés reçoivent un npm deprecate qui pointe vers @recit/motion |
| Markdown par défaut ? | Oui, dans Récit. Le Markdown est la porte d'entrée commune, et le JSON reste supporté (deck.json) comme format machine et MCP |
| Format hybride, doc vers film ? | Oui, c'est la promesse de Récit : un même story.md se rend en deck (recit build) et en vidéo (recit render). Une page MDX de la doc peut intégrer l'un ou l'autre (<StoryEmbed slug format="motion" />) |
2. Phases
Les phases V2 à V4 peuvent démarrer dans le dépôt emaki, avant l'import, comme le prévoyait
emaki/PLAN.md. Il faut juste écrire la spec Markdown dans la syntaxe Récit (V3).
V1 — Import dans le fork (S/M), reprend les phases 1 et 2 d'emaki
Fait le 24/10/2026 (journal.md §2) : préparé dans une copie du dépôt, puis rapatrié. Dépôt
réuni vérifié : pnpm check, build et typecheck de 23 projets, 1 938 tests, 16 goldens, 139 e2e.
- Dans emaki (sans git) : vérifier
pnpm build && pnpm test && pnpm goldens:checkavant la copie. - Copier chaque package dans son dossier final
(
packages/formats/{motion,motion-blocks,motion-render},packages/mcp). L'origine (emaki@1b86ccd, l'auteur) est dansNOTICE.md, leREADME.mdet l'en-tête desrc/index.tsde chaque package ; les commits sont faits par l'utilisateur.extractest fusionné dans@recit/motion; le studio n'est pas importé (squelettepackages/tools/studio). - Ne pas copier l'outillage d'emaki (
turbo.json,tsconfig.base.json,pnpm-workspace.yaml,.changeset/, lockfile) : nx, la base TS et le catalogue du fork le remplacent. -
allowBuilds:esbuild: truesuffit pour Remotion, rien d'autre n'a été ajouté. - Conserver la condition d'export
development. Elle devient la convention de tous les@recit/*(voir03-packaging.md). - Dépendances :
- React 19.2.8 et zod 4.6.5 viennent du catalogue ;
react >=18passe à>=19.2.7;framer-motionest remplacé parmotion(motion/react) dès l'import : une seule stack ;- Remotion 4 et le SDK MCP restent locaux à
motion-renderetmcp; - vérifier
minimumReleaseAge. - Reste ouvert : Remotion impose une seconde version de zod (4.4.3) dans l'arbre de
motion-render(@remotion/studio). Une autre copie existait par le pair optionnelsite >=0.93.0de@recit/site-stories, résolu sur la copie npm du framework de sites au lieu du fork (antérieur à P4). Relier le fork casse le typecheck de@recit/site-stories(les sources du fork ne passent pas la config TypeScript de Récit) : à traiter avec TE-3 deaudit.md. Le 25/10, aprèspnpm install, zod 4.4.3 n'est plus tiré que par Remotion.
- Chrome Headless Shell (environ 93 Mo) : mettre en cache en CI (
~/.cache/remotion), ou sauter le rendu MP4 en CI et ne garder que les goldens PNG. Reporté à V2 (pas encore de job motion en CI). -
.gitignore:out/(rendus MP4 derecit render). - Goldens : scripts
goldensetgoldens:checkde@recit/motion-render(pnpm -F @recit/motion-render goldens:check), plutôt que des scripts racine. Les 16 PNG ont été régénérés après le passage des fixturesshowcase-*en Lorem Ipsum. Ils dépendent des polices installées sur la machine (tolérance de 0,2 % pour le lissage) : à figer dans un conteneur en CI (§4). -
AGENTS.mdd'emaki : généré danspackages/formats/motion/AGENTS.md, pas à la racine (qui reste le guide du framework de sites). Reste à le référencer depuiscontext.md. - MCP :
claude mcp add recit -- npx recit mcp serve(fusion du CLI, V6). Le binairerecit-mcpde@recit/mcpreste disponible.
Critère de sortie : depuis la racine du fork,
nx run-many -t build test -p '@recit/motion*' @recit/mcp et goldens:check passent (atteint le
24/10). recit render fixtures/hello.deck.json produit le même MP4 que emaki render avant
l'import : reporté à la fusion du CLI d'emaki (V6), mis de côté dans scratch-emaki-cli/.
V2 — Un seul chargeur de deck (S), reprend la phase 3
Fait : @recit/motion/source (formatOf, parseDeckSource) et @recit/motion/node
(readDeckFile, writeDeckFile), utilisés par motion-render (loadDeck, script des goldens) et
mcp (apply_ops, build_deck, extract). Le CLI et le studio suivront en V6.
Sans changement par rapport à emaki/PLAN.md :
formatOf,parseDeckSource,readDeckFileetwriteDeckFileremplacent les 5JSON.parse(readFileSync(…))(cli, mcpreadDeck, renderloadDeck, studiostore.ts) et les 4writeFileSync(JSON.stringify(…)).- Les tests existants passent sans modification.
V3 — Spec Markdown du format motion (S), reprend la phase 4 avec la syntaxe Récit
C'est le changement principal par rapport au plan emaki : pas de seconde syntaxe. Un récit
format: motion s'écrit comme un deck :
Markdown
- Les attributs de scène sur le séparateur (
type,id,hold,dur,unit,number,kicker…) remplacent<!-- type: …, id: … -->. Ce commentaire reste accepté comme alias (voir02-architecture.md, section 3). - Le tableau d'inférence est celui d'emaki, inchangé (
title,chapter,statement,list,compare-bars,statjamais inféré,ui-*en yaml, échappatoire```yaml/```json). Les équivalences avec les layouts de deck sont dans02-architecture.md, section 4. - Pas d'inférence ambiguë : une erreur avec numéro de ligne
(
story.md:14 (stat): label is required). - Rédiger la spec dans
sites/docs/pages/formats/motion.mdx(en anglais). - La valider sur
hello,saas,showcase-saasetshowcase-warmavant de coder (packages/formats/motion/fixtures/*.story.md).
V4 — Parseur Markdown → Deck (M), reprend la phase 5
Au lieu d'un @emaki/markdown qui reparse avec son propre remark, on réutilise @recit/core :
Code
-
storyToHandover(story)dans@recit/motion. Il litscene.mdastetscene.attrs, applique le tableau d'inférence et produit unsourceMap(scène → ligne) à partir descene.source. -
parseMarkdownDeck(text)remappeHandoverIssue.scenevers des lignes Markdown. - Le brancher dans
parseDeckSource(text, "md")(V2). - Le lint texte (glyphes non couverts par les polices du thème) s'applique aussi au
.md. -
:::notes→ champvoiceover(oucaption) du schémaDeck. C'est un ajout sans rupture, enversion: 1.
Tests :
- Équivalence :
hello.story.md,saas.story.mdetshowcase-*.story.mddonnent unDeckégal (toEqual) à leur.deck.json. - Goldens : les PNG rendus depuis le
.mdsont identiques aux goldens actuels. - Erreurs : un cas par règle d'inférence, avec le bon numéro de ligne.
V5 — Sérialiseur Deck → Markdown et réécriture (M/L), reprend la phase 6
-
deckToMarkdown(deck): sortie canonique dans la syntaxe Récit (attributs de séparateur,id=toujours émis, bloc yaml pour les props sans forme naturelle). - Propriété testée :
parse(deckToMarkdown(deck))est égal àdeckpour toutes les fixtures. - Réécriture : v1 canonique, documentée (faite :
writeDeckFilesur un.md). v2 chirurgicale viascene.source: seules les scènes touchées parreplace,patch,insertAfteretremovesont réécrites, le reste est conservé à l'octet près. - CLI :
recit convert deck.json --to mdet l'inverse,recit init --format motion.
V6 — Intégration à Récit : CLI, site, MCP, studio (M), reprend la phase 7
CLI (@recit/cli) :
-
recit render story.md [--aspect 9:16] [--output out/]charge@recit/motion-renderà la demande. Si le package manque, le message d'erreur explique quoi installer. -
recit validate,recit dur(durée totale) etrecit extractreprennent les commandesemaki. -
recit dev story.md: siformat: motion, prévisualisation dans le navigateur avec le moteur de blocs et le timing, live reload. Le lecteur est celui de@recit/motion-blocks(MotionFrame,filmTimeline) :recit dev,recit dur, la page et le MP4 coupent sur les mêmes images.recit renderlitrecit.configcommevalidateetdur.
Site et le framework de sites (@recit/site-stories) :
-
<StoryEmbed slug="hello" format="motion" />joue la vidéo dans la page avec le moteur navigateur de@recit/motion-blocks, sans Remotion ni MP4. Un MP4 prérendu peut être fourni enposter/srcpour le SSR. Reste : pas de job CI qui rend les MP4 de/video, et pas de redirectionvideo.recit.devconfigurée. - Route
/present/<slug>: lecteur plein écran pour un récitmotion. - Onglet Formats › Motion dans
sites/docset sitesites/motion(motion.recit.dev,video.recit.deven alias, voir07-domaines.md) : spec (V3), galerie d'exemples, aspects16:9,1:1et9:16. - CSS des blocs : même stratégie que pour le deck, limitée au wrapper avec l'ordre des layers.
MCP (@recit/mcp) :
- Tous les outils
deckPathacceptent.md(fait en V2 :apply_opsetrenderlisent parreadDeckFile,outen.mdécrit du Markdown). -
build_deckacceptemarkdown. Ajouter l'outildescribe_format(format)(JSON Schema et spec Markdown,09-langues-et-json.md§7). La version du serveur MCP est lue dans sonpackage.json. - Le guide
emaki://guidedevientrecit://guide/motion. Ajouterrecit://guide/deckpour les decks.
Studio (@recit/studio, privé ; squelette, import du studio d'emaki différé tant qu'il est simulé,
journal.md §5) :
- Ouverture et hot-reload d'un
.md, Monaco en modemarkdown, erreurs avec numéro de ligne.
V7 — Renommage et unification (M)
- Thèmes : exprimer
warm-editorialetsaas-productcomme thèmes@recit/theme(tokens HSL partagés). Un récit garde le même thème en deck et en vidéo. Fait :RECIT_THEMESde@recit/theme(tokens HSL, polices, palette de données, échelle typographique motion),toMotionThemedans@recit/theme/motion,@recit/motion-blocksn'a plus de copie. Le sélecteur esttheme: <nom>(front matter) outheme.name(recit.config) ; le film le lit aprèsmotion.theme. Goldens inchangés (0,000 %). Doc :docs.recit.dev/theme/named-themes. -
npm deprecate "@emaki/*" "moved to @recit/motion": à faire par l'auteur d'emaki (mainteneur npmyathavan), pas par Récit. Nous n'avons pas les droits sur le scope@emakiet Récit ne publie rien sur npm avant P5.
3. Ordre
Code
V4 dépend de @recit/core avec scene.mdast, scene.attrs et scene.source, donc de la phase 1
de la roadmap (import de presentation-engine). Si on commence dans emaki, un split et un remark
minimal font office de bouchon, remplacé ensuite par compileStory.
4. Risques propres à la vidéo
| Risque | Parade |
|---|---|
| Remotion et Chrome alourdissent tout le monorepo | @recit/motion-render isolé, dépendance optionnelle de la CLI, rendu MP4 hors CI par défaut |
| Licence Remotion (licence d'entreprise au-delà d'une certaine taille d'équipe) | la vérifier avant publication et la documenter dans NOTICE.md. Le chemin navigateur n'en dépend pas |
| Deux syntaxes Markdown qui divergent | une seule : celle de Récit. Le commentaire HTML n'est qu'un alias |
| Un layout de deck sans équivalent vidéo | erreur explicite en v1, puis rendu en image fixe (capture de la slide) en v2 |
framer-motion contre motion en double dans le bundle | motion seul dès l'import (V1), size-limit sur @recit/site-stories |
| Goldens instables entre macOS et la CI Linux | goldens générés en CI (conteneur fixe), tolérance de diff documentée |