From f40a5987f9bfd9e2a08ad3c8a15ece8786713bd8 Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Mon, 31 Aug 2026 20:00:55 +0000 Subject: [PATCH] Reforca ancora de fidelidade visual de personagens e cenario em 100% dos quadros e adiciona re-sincronizacao de texto editado na regeneracao --- public/app.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- server.js | 44 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/public/app.js b/public/app.js index 19c5c1e..34a8c00 100644 --- a/public/app.js +++ b/public/app.js @@ -5275,7 +5275,7 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.` }); }; - // Regenerar um único painel + // Regenerar um único painel inteligente (considera textos/legendas alterados e preserva âncora visual de personagens) const regenerateSinglePanel = async (index, panelCard, imgElement, btnRegen) => { const panel = generatedPanelsData[index]; if (!panel) return; @@ -5285,13 +5285,52 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.` btnRegen.textContent = '⏳'; imgElement.style.opacity = '0.4'; - const promptToUse = panel.image_prompt || 'cute children illustration'; - const fullPrompt = `${promptToUse}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`; + // Captura o diálogo/texto ATUAL que está escrito na textarea do quadrinho (caso o usuário tenha editado) + const textarea = panelCard.querySelector('.comic-caption-input'); + const currentDialogue = textarea ? textarea.value.trim() : (panel.dialogue || ''); + panel.dialogue = currentDialogue; + + // Captura o prompt visual se o mini-editor tiver sido alterado + const promptInput = panelCard.querySelector('.comic-prompt-details textarea'); + let promptToUse = promptInput ? promptInput.value.trim() : (panel.image_prompt || ''); + + // Se o diálogo tiver sido preenchido/alterado, re-sincronizamos a cena visual com o novo texto + if (currentDialogue) { + try { + const resyncResp = await fetch('/api/comics/resync-frame-prompt', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + currentDialogue, + oldPrompt: promptToUse, + character_description: currentComicsCharDescEnglish, + cenario: comicsCenarioSelect ? comicsCenarioSelect.value : '' + }) + }); + if (resyncResp.ok) { + const resyncData = await resyncResp.json(); + if (resyncData.image_prompt) { + promptToUse = resyncData.image_prompt; + panel.image_prompt = promptToUse; + if (promptInput) promptInput.value = promptToUse; + } + } + } catch (resyncErr) { + console.warn('Re-sincronização de prompt ao regenerar:', resyncErr.message); + } + } + + // Monta o prompt reforçado com a Âncora Visual Física de Personagens e Estilo 3D Pixar + let anchorPart = ''; + if (currentComicsCharDescEnglish) { + anchorPart = `CHARACTER ANCHOR: [${currentComicsCharDescEnglish}]. `; + } + const fullPrompt = `${anchorPart}${promptToUse}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`; const resp = await fetch('/api/comics/generate-frame', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prompt: fullPrompt }) + body: JSON.stringify({ prompt: fullPrompt, proporcao: selectedComicsRatio }) }); if (!resp.ok) { @@ -5303,6 +5342,10 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.` panel.imageUrl = data.imageUrl; imgElement.src = data.imageUrl + '?v=' + Date.now(); imgElement.style.opacity = '1'; + + // Auto-salva o projeto atualizado no banco + saveComicsProjectFlow(false).catch(e => console.warn('Auto-save pós regeneração:', e.message)); + } catch (err) { console.error('Erro ao regenerar painel:', err); await showCustomAlert('Erro', 'Não foi possível regenerar o quadrinho: ' + err.message); diff --git a/server.js b/server.js index 8e3bc3a..4ea885a 100644 --- a/server.js +++ b/server.js @@ -533,11 +533,11 @@ Cenário/Local principal da nova fase: ${cenario}. Gere um JSON perfeitamente válido contendo: { "title": "${continuationType === 'extend' ? 'Manter o título original ou propor um subtítulo condizente' : 'Novo Título da História'}", - "character_description": "${character_description || 'Descrição visual constante dos personagens em inglês'}", + "character_description": "${character_description || 'Descrição física rigorosamente constante e fixa dos personagens em inglês (roupas fixas, cabelos, cores)'}", "panels": [ { "panel_number": ${startPanelNum}, - "image_prompt": "Prompt visual detalhado em inglês para o gerador de imagens da MiniMax descrevendo a cena deste quadrinho. IMPORTANTE PARA CONSISTÊNCIA: Substitua TODOS os nomes de personagens por sua descrição física minuciosa em inglês (obtida de character_description) em cada quadrinho que aparecerem (ex: em vez de apenas escrever 'Lucas', escreva a descrição detalhada dele com roupas e cabelos específicos, etc). O estilo deve ser de ilustração de livro infantil de alta qualidade, 3D Pixar, traço limpo.", + "image_prompt": "Prompt visual minucioso em inglês para o gerador de imagens. REGRA RÍGIDA DE CONSISTÊNCIA: Substitua nomes de personagens por sua descrição física EXATA e FIXA em inglês (do character_description) em TODOS os quadrinhos onde aparecerem (mesmas roupas, mesma cor de cabelo, mesmas características). Descreva a ação específica do quadrinho mantendo o mesmo estilo visual 3D Pixar.", "dialogue": "Falas do quadrinho em português (ex: 'Lucas: Veja aquilo!') com NO MÁXIMO 10 palavras, ou narração curta caso não tenha balões." }, ... @@ -556,11 +556,11 @@ Cenário/Local principal: ${cenario}. Gere um JSON perfeitamente válido contendo: { "title": "Título da História", - "character_description": "Descrição visual constante dos personagens em inglês (para usar em geradores de imagens, detalhando roupas específicas, cores, características físicas de forma fixa para manter a consistência).", + "character_description": "Descrição física rigorosamente detalhada e fixa dos personagens em inglês (especifique roupas fixas, cores de camiseta/vestido, tipo/cor de cabelo, tom de pele e características físicas fixas para NUNCA mudarem entre os quadrinhos).", "panels": [ { "panel_number": 1, - "image_prompt": "Prompt visual detalhado em inglês para o gerador de imagens da MiniMax descrevendo a cena deste quadrinho. IMPORTANTE PARA CONSISTÊNCIA: Substitua TODOS os nomes de personagens por sua descrição física minuciosa em inglês (do character_description) em cada quadrinho que aparecerem (ex: não escreva apenas 'Mariana', escreva a descrição dela detalhando cabelo e roupas de forma fixa e idêntica em todos os quadrinhos). O estilo deve ser de ilustração de livro infantil de alta qualidade, 3D Pixar, traço limpo.", + "image_prompt": "Prompt visual minucioso em inglês para o gerador de imagens da MiniMax. REGRA RÍGIDA DE CONSISTÊNCIA: Substitua TODOS os nomes de personagens por sua descrição física EXATA e FIXA em inglês (do character_description) em TODOS os quadrinhos em que aparecerem (mesmas roupas fixas, cabelos e características de forma idêntica em 100% das imagens). Descreva a cena específica mantendo a arte 3D Pixar infantil de traço limpo.", "dialogue": "Falas do quadrinho em português (ex: 'Lucas: Veja aquilo!') com NO MÁXIMO 10 palavras, ou narração curta caso não tenha balões." }, ... @@ -601,6 +601,42 @@ Responda APENAS o JSON válido, sem qualquer texto explicativo antes ou depois.` } }); +// 1b. Re-sincronizar o prompt visual de um quadrinho quando o texto/fala for alterado na regeneração +app.post('/api/comics/resync-frame-prompt', requireAuth, async (req, res) => { + const { currentDialogue, oldPrompt, character_description, cenario } = req.body; + if (!currentDialogue || !currentDialogue.trim()) { + return res.json({ image_prompt: oldPrompt }); + } + + try { + const prompt = `Você é um diretor de arte de histórias em quadrinhos infantis 3D estilo Pixar. +A professora alterou o diálogo/texto do quadrinho para: "${currentDialogue}". +Descrição física fixa dos personagens (em inglês): "${character_description || ''}". +Cenário fixo da história: "${cenario || ''}". +Prompt antigo da cena: "${oldPrompt || ''}". + +Re-escreva a descrição visual da cena (image_prompt) em inglês para o gerador de imagens. +REGRAS RÍGIDAS DE FIDELIDADE VISUAL: +1. Mantenha os mesmos personagens com a MESMA descrição física e roupas exatas (do character_description). +2. Mantenha o mesmo estilo de iluminação e elementos do cenário (${cenario}). +3. Adapte as ações, gestos e expressões faciais para combinarem EXATAMENTE com a nova fala/texto editado: "${currentDialogue}". + +Retorne APENAS a nova descrição visual da cena em inglês, sem explicações.`; + + const r = await callMinimax({ + messages: [{ role: 'user', content: prompt }], + temperature: 0.3, + max_tokens: 300 + }); + + const newPrompt = r.text.trim(); + res.json({ image_prompt: newPrompt || oldPrompt }); + } catch (err) { + console.warn('[Comics Resync] Falha ao re-sincronizar prompt, usando original:', err.message); + res.json({ image_prompt: oldPrompt }); + } +}); + // 2. Gerar uma imagem individual para um quadrinho usando MiniMax image-01 com Fallback Automático app.post('/api/comics/generate-frame', requireAuth, async (req, res) => { const { prompt, proporcao } = req.body;