Corrigindo salvamento de múltiplos projetos e continuidade de quadrinhos com consistência de personagens

This commit is contained in:
2026-06-03 17:54:59 +00:00
parent 7fd500eec1
commit 133bf185e9
3 changed files with 127 additions and 70 deletions
+99 -60
View File
@@ -2097,56 +2097,47 @@ const initApp = () => {
const mode = btn.dataset.mode;
const text = userInput.value.trim();
// Se for modo POETRY ou TEXT, ou se o input estiver vazio, preenchemos o input com a sugestão correspondente sem enviar
const isTextOrPoetry = (mode === 'POETRY' || mode === 'TEXT');
let suggestion = "";
let baseText = text;
if (!text || isTextOrPoetry) {
let suggestion = "";
let baseText = text;
// Se o texto atual já for uma das sugestões padrão, limpamos para não duplicar
const suggestionsList = [
"Crie uma música pedagógica alegre e animada sobre ",
"Crie um roteiro detalhado para um vídeo educativo sobre ",
"Crie uma ilustração pedagógica com cores vibrantes mostrando ",
"Escreva uma poesia infantil rimada e educativa sobre ",
"Escreva uma história educativa infantil curta sobre "
];
for (const sugg of suggestionsList) {
if (baseText.startsWith(sugg)) {
baseText = baseText.replace(sugg, "");
break;
}
// Se o texto atual já for uma das sugestões padrão, limpamos para não duplicar
const suggestionsList = [
"Crie uma música pedagógica alegre e animada sobre ",
"Crie um roteiro detalhado para um vídeo educativo sobre ",
"Crie uma ilustração pedagógica com cores vibrantes mostrando ",
"Escreva uma poesia infantil rimada e educativa sobre ",
"Escreva uma história educativa infantil curta sobre "
];
for (const sugg of suggestionsList) {
if (baseText.startsWith(sugg)) {
baseText = baseText.replace(sugg, "").trim();
break;
}
if (mode === 'AUDIO') {
suggestion = "Crie uma música pedagógica alegre e animada sobre ";
} else if (mode === 'VIDEO') {
suggestion = "Crie um roteiro detalhado para um vídeo educativo sobre ";
} else if (mode === 'IMAGE') {
suggestion = "Crie uma ilustração pedagógica com cores vibrantes mostrando ";
} else if (mode === 'POETRY') {
suggestion = "Escreva uma poesia infantil rimada e educativa sobre ";
} else if (mode === 'TEXT') {
suggestion = "Escreva uma história educativa infantil curta sobre ";
}
userInput.value = suggestion + baseText;
userInput.focus();
// Mover cursor para o final do texto
userInput.selectionStart = userInput.selectionEnd = userInput.value.length;
// Ajustar altura do textarea para acomodar a sugestão
userInput.style.height = 'auto';
userInput.style.height = userInput.scrollHeight + 'px';
btnSend.disabled = false;
return;
}
// Se houver texto e for outro modo (AUDIO, VIDEO, IMAGE), envia imediatamente
handleSendMessage(text, false, null, mode);
if (mode === 'AUDIO') {
suggestion = "Crie uma música pedagógica alegre e animada sobre ";
} else if (mode === 'VIDEO') {
suggestion = "Crie um roteiro detalhado para um vídeo educativo sobre ";
} else if (mode === 'IMAGE') {
suggestion = "Crie uma ilustração pedagógica com cores vibrantes mostrando ";
} else if (mode === 'POETRY') {
suggestion = "Escreva uma poesia infantil rimada e educativa sobre ";
} else if (mode === 'TEXT') {
suggestion = "Escreva uma história educativa infantil curta sobre ";
}
userInput.value = suggestion + baseText;
userInput.focus();
// Mover cursor para o final do texto
userInput.selectionStart = userInput.selectionEnd = userInput.value.length;
// Ajustar altura do textarea para acomodar a sugestão
userInput.style.height = 'auto';
userInput.style.height = userInput.scrollHeight + 'px';
if (btnSend) btnSend.disabled = false;
});
});
@@ -3515,6 +3506,7 @@ const initApp = () => {
const btnSaveComicsProject = document.getElementById('btnSaveComicsProject');
let currentComicsProjectId = null;
let currentComicsCharDescEnglish = '';
let selectedComicsQty = 5;
let selectedComicsBubbles = true;
let selectedComicsRatio = '16:9';
@@ -3617,6 +3609,13 @@ const initApp = () => {
// --- GERENCIADOR DE PROJETOS DE HQ ---
const startNewComicsProject = () => {
currentComicsProjectId = null;
currentComicsCharDescEnglish = '';
const modeContainer = document.getElementById('comicsGenerationModeContainer');
if (modeContainer) modeContainer.style.display = 'none';
const radioNew = document.querySelector('input[name="comicsGenMode"][value="new_story"]');
if (radioNew) radioNew.checked = true;
if (comicsProjectSelect) comicsProjectSelect.value = '';
if (comicsProjectTitleInput) comicsProjectTitleInput.value = '';
if (btnDeleteComicsProject) btnDeleteComicsProject.style.display = 'none';
@@ -3699,6 +3698,11 @@ const initApp = () => {
const { project, panels } = await resp.json();
currentComicsProjectId = project.id;
currentComicsCharDescEnglish = project.character_description_english || '';
const modeContainer = document.getElementById('comicsGenerationModeContainer');
if (modeContainer) modeContainer.style.display = 'flex';
if (comicsProjectTitleInput) comicsProjectTitleInput.value = project.titulo || '';
comicsTemaInput.value = project.tema || '';
@@ -3852,9 +3856,10 @@ const initApp = () => {
cenario,
proporcao: selectedComicsRatio,
character_description_global: JSON.stringify(characters),
character_description_english: currentComicsCharDescEnglish || '',
panels: generatedPanelsData.map(p => ({
panel_number: p.panel_number,
image_url: p.imageUrl,
image_url: p.imageUrl || p.image_url || '',
image_prompt: p.image_prompt || '',
dialogue: p.dialogue || ''
}))
@@ -4009,17 +4014,32 @@ const initApp = () => {
try {
// 1. Gerar Roteiro
const genMode = currentComicsProjectId ? (document.querySelector('input[name="comicsGenMode"]:checked')?.value || 'new_story') : 'new_story';
const requestBody = {
tema,
quantidade: selectedComicsQty,
baloes: selectedComicsBubbles,
proporcao: selectedComicsRatio,
personagens,
cenario
};
if (currentComicsProjectId) {
requestBody.continuationType = genMode;
requestBody.character_description = currentComicsCharDescEnglish;
requestBody.existingPanels = generatedPanelsData.map(p => ({
panel_number: p.panel_number,
image_url: p.imageUrl || p.image_url || '',
image_prompt: p.image_prompt || '',
dialogue: p.dialogue || ''
}));
}
const scriptResp = await fetch('/api/comics/generate-script', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tema,
quantidade: selectedComicsQty,
baloes: selectedComicsBubbles,
proporcao: selectedComicsRatio,
personagens,
cenario
})
body: JSON.stringify(requestBody)
});
if (!scriptResp.ok) {
@@ -4030,8 +4050,14 @@ const initApp = () => {
const scriptData = await scriptResp.json();
comicsLoaderProgress.style.width = '15%';
if (scriptData.character_description) {
currentComicsCharDescEnglish = scriptData.character_description;
}
// 2. Gerar cada quadrinho sequencialmente
generatedPanelsData = [];
if (genMode !== 'extend') {
generatedPanelsData = [];
}
const totalPanels = scriptData.panels.length;
for (let i = 0; i < totalPanels; i++) {
@@ -4058,12 +4084,13 @@ const initApp = () => {
generatedPanelsData.push({
panel_number: panel.panel_number,
imageUrl: frameData.imageUrl,
dialogue: panel.dialogue || ''
dialogue: panel.dialogue || '',
image_prompt: panel.image_prompt || ''
});
}
// Renderizar quadrinhos na tela
comicsResultTitle.textContent = scriptData.title || 'Fábrica de Quadrinhos Camila';
// Renderizar quadrinhos na tela (todos, incluindo os anteriores caso tenha sido extensão)
comicsResultTitle.textContent = scriptData.title || comicsResultTitle.textContent || 'Fábrica de Quadrinhos Camila';
comicsGridContainer.innerHTML = '';
generatedPanelsData.forEach(panel => {
@@ -4075,7 +4102,7 @@ const initApp = () => {
const img = document.createElement('img');
img.className = 'comic-panel-image';
img.src = panel.imageUrl;
img.src = panel.imageUrl || panel.image_url;
img.alt = `Quadro ${panel.panel_number}`;
imgContainer.appendChild(img);
@@ -4106,6 +4133,18 @@ const initApp = () => {
comicsGridContainer.appendChild(panelCard);
});
// Se for uma nova história derivada de um projeto antigo, limpamos o ID para virar um novo projeto independente ao salvar
if (genMode === 'new_story' && currentComicsProjectId) {
currentComicsProjectId = null;
if (comicsProjectSelect) comicsProjectSelect.value = '';
const modeContainer = document.getElementById('comicsGenerationModeContainer');
if (modeContainer) modeContainer.style.display = 'none';
if (comicsProjectTitleInput) {
comicsProjectTitleInput.value = scriptData.title || (comicsProjectTitleInput.value + ' (Derivado)');
}
if (btnDeleteComicsProject) btnDeleteComicsProject.style.display = 'none';
}
comicsGenerateLoader.style.display = 'none';
comicsResultArea.style.display = 'flex';
btnGenerateComics.disabled = false;
+15
View File
@@ -761,6 +761,21 @@
<input type="text" id="comicsProjectTitleInput" class="obs-input" placeholder="Título do Projeto (Ex: Lucas e Mariana no Parque)" style="flex: 1; margin: 0; padding: 8px 12px; font-size: 0.9rem;">
<button type="button" id="btnSaveComicsProject" class="btn-music-option" style="padding: 8px 14px; font-size: 0.82rem; border-color: #10a37f; color: white; background: #10a37f; margin: 0; white-space: nowrap; font-weight: 600;">💾 Salvar</button>
</div>
<!-- MODO DE CONTINUIDADE (Apenas quando um projeto é carregado) -->
<div id="comicsGenerationModeContainer" style="display: none; border-top: 1px dashed var(--border-light); padding-top: 10px; flex-direction: column; gap: 8px;">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600;">AÇÃO PARA A PRÓXIMA GERAÇÃO:</label>
<div style="display: flex; flex-direction: column; gap: 8px; background: rgba(0,0,0,0.15); padding: 10px; border-radius: 8px;">
<label style="display: flex; align-items: center; gap: 8px; font-size: 0.85rem; cursor: pointer; color: var(--text-primary);">
<input type="radio" name="comicsGenMode" value="new_story" checked style="cursor: pointer;">
<span>🆕 <strong>Criar Nova História baseada neste Projeto</strong> (Começa do Quadro 1 com os mesmos personagens, salvando como outro projeto)</span>
</label>
<label style="display: flex; align-items: center; gap: 8px; font-size: 0.85rem; cursor: pointer; color: var(--text-primary);">
<input type="radio" name="comicsGenMode" value="extend" style="cursor: pointer;">
<span>➡️ <strong>Continuar a História Existente (Estender)</strong> (Cria mais 5 quadros sequenciais, dando sequência a esta história)</span>
</label>
</div>
</div>
</div>
<!-- TEMA -->