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>
Chat IA:
- Scroll natif avec auto-scroll vers le dernier message
- Input toujours visible en bas du panneau
- Remplace ScrollArea (ref cassé) par div overflow-y-auto
Panneaux redimensionnables:
- Bottom panel (timeline): drag vertical pour ajuster la hauteur (120-600px)
- Right panel (assistant): drag horizontal pour ajuster la largeur (280-700px)
- Handles visuels avec feedback hover
Timeline:
- Molette = zoom (sans Ctrl), Shift+molette = scroll horizontal
- Zoom max augmenté à 20x
- Clic droit = menu contextuel (aller à frame, set IN/OUT, effacer)
- data-seq-id sur les blocs de séquence pour identification
Génération IA:
- Meilleur error handling avec message dans le toast (8s durée)
- Console.error pour debug navigateur
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cache utilisateur en mémoire (60s TTL) : élimine la query
getUserByOpenId (~300ms) de chaque requête authentifiée
- Suppression du upsertUser(lastSignedIn) à chaque requête
- staleTime sur toutes les queries (auth.me: 60s, workspace: 30s,
frames: 120s, home: 15s)
- refetchOnWindowFocus: false partout
Résultat: auth.me 300ms -> 70ms, workspace load 2.5s -> 0.8s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
L'authenticateRequest appelait getUserInfoWithJwt sur l'API Manus
à chaque requête, même pour les utilisateurs déjà en base.
Maintenant: si l'utilisateur existe en DB, on le retourne directement
sans appel réseau externe. L'appel OAuth ne se fait que pour les
nouveaux utilisateurs non-locaux.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Chargement bulk des URLs de frames (1 query au lieu de 1/frame)
- Preload des 30 prochaines frames pendant la lecture
- Cache navigateur activé sur le proxy S3 (max-age=3600, immutable)
- Fallback query tRPC uniquement si la map n'a pas la frame
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>