L'outil d'annotation existant ne servait qu'à créer un masque global.
Maintenant chaque masque peut déclencher un inpainting IA qui
modifie UNIQUEMENT la zone sélectionnée.
Backend:
- convertMaskForOpenAI(): convertit notre format (blanc=édit/noir=préserve)
vers format OpenAI (alpha=0=édit/opaque=préserve)
- Auto-redimensionne le mask aux dims de l'image source
- generateImage() accepte maintenant un paramètre maskUrl
- OpenAI images.edits utilise le param "mask" + champ "image" (singulier)
pour le mode inpainting
- Nouveau endpoint generation.inpaintZone(frameId, maskUrl, prompt, sourceType)
- sourceType: original / bg (regen actif) / fg (perso actif) / composite
- Crée une nouvelle variante du type approprié (Module 1)
- Synchronise les champs legacy
Frontend (AnnotationCanvas):
- Nouveau bouton "Inpainter zone" dans la toolbar
- Form dropdown avec sélecteur de source (original/composite/bg/fg)
et prompt textarea
- handleInpaint: upload du masque + appel inpaintZone + new variant
- Sauve masque (bouton existant renommé "Sauver masque") séparé de l'inpainting
- AnnotationCanvas reçoit projectId + frameIndex pour pouvoir appeler les routes
Workflow utilisateur:
1. Mode "Annoter" dans le viewport (sur frame originale)
2. Dessine au pinceau/rectangle/lasso la zone à modifier
3. Click "Inpainter zone"
4. Choisit source (original/composite/etc.) + écrit le prompt
5. Click "Lancer inpainting"
6. OpenAI génère uniquement la zone masquée
7. Nouvelle variante créée et visible dans la galerie M1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Format ratio (60):
- OpenAI gpt-image-1: choix automatique de size (1024x1024 / 1536x1024 /
1024x1536) selon ratio cible le plus proche
- Crop sharp post-génération pour matcher EXACTEMENT le ratio source
- segmentationService passe les dimensions du projet (width/height)
à regenerateBackground et regenerateCharacter
- Routes generation.* récupèrent project.width/height depuis DB
- Testé: frame 25 (854x480) -> bg généré en 854x480 exact
Redirect bug (61):
- DashboardLayout: navigate("/login") déplacé du render vers useEffect
(anti-pattern React fix)
- main.tsx redirectToLoginIfUnauthorized: préserve l'URL d'origine
dans ?return=...
- Login.tsx onSuccess: redirige vers ?return URL au lieu de "/"
- Plus de retour à l'accueil après refresh ou opération
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- OPENAI_API_KEY ajouté dans .env.docker
- imageGeneration.ts: tente Gemini d'abord, fallback OpenAI sur erreur
- Support images.edits (multipart avec ref image) pour gpt-image-1
- Support images.generations (text-only) si pas de ref
- Provider trace dans GenerateImageResponse pour debug
- Testé: pipeline complet frame 25
* Fond Ghibli: 21s via OpenAI (Gemini 429) -> 1.6MB PNG
* Personnage moderne: 26s via OpenAI -> 1.6MB PNG RGBA
* Composite final: 1s via sharp -> 2.6MB PNG
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Modèle corrigé: gemini-2.0-flash-exp (supprimé) -> gemini-2.5-flash-image
- Pipeline de génération testé et fonctionnel
(bloqué temporairement par quota API gratuit)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>