Feature: Fabrica de Quadrinhos profissional com geracao paralela, modo apresentacao fullscreen, storyboard interativo, exportacao ZIP e adaptacao mobile
This commit is contained in:
+392
-55
@@ -4442,6 +4442,8 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
comicsCharListContainer.appendChild(row);
|
comicsCharListContainer.appendChild(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const btnImportTurmaChars = document.getElementById('btnImportTurmaChars');
|
||||||
|
|
||||||
if (btnAddHumanChar) {
|
if (btnAddHumanChar) {
|
||||||
btnAddHumanChar.addEventListener('click', () => {
|
btnAddHumanChar.addEventListener('click', () => {
|
||||||
addCharacterRow('', '', false);
|
addCharacterRow('', '', false);
|
||||||
@@ -4454,6 +4456,37 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (btnImportTurmaChars) {
|
||||||
|
btnImportTurmaChars.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
btnImportTurmaChars.disabled = true;
|
||||||
|
btnImportTurmaChars.textContent = '⏳ Carregando...';
|
||||||
|
const res = await fetch('/api/alunos');
|
||||||
|
if (!res.ok) throw new Error('Falha ao obter lista de alunos');
|
||||||
|
const alunos = await res.json();
|
||||||
|
if (!alunos || alunos.length === 0) {
|
||||||
|
await showCustomAlert('Nenhum Aluno', 'Cadastre alunos na seção "Minha Turma" para importá-los automaticamente.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adiciona os alunos como personagens (até 4 para manter a história focada)
|
||||||
|
const selecionados = alunos.slice(0, 4);
|
||||||
|
comicsCharListContainer.innerHTML = '';
|
||||||
|
selecionados.forEach(aluno => {
|
||||||
|
const generoProvavel = aluno.nome.trim().endsWith('a') ? 'Menina' : 'Menino';
|
||||||
|
addCharacterRow(generoProvavel, aluno.nome.trim(), false);
|
||||||
|
});
|
||||||
|
await showCustomAlert('Turma Importada!', `${selecionados.length} alunos foram adicionados como personagens da história.`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erro ao importar alunos:', err);
|
||||||
|
await showCustomAlert('Erro', 'Não foi possível carregar a lista de alunos: ' + err.message);
|
||||||
|
} finally {
|
||||||
|
btnImportTurmaChars.disabled = false;
|
||||||
|
btnImportTurmaChars.textContent = '+ 🏫 Importar da Turma';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Exibir/ocultar cenário personalizado
|
// Exibir/ocultar cenário personalizado
|
||||||
if (comicsCenarioSelect) {
|
if (comicsCenarioSelect) {
|
||||||
comicsCenarioSelect.addEventListener('change', () => {
|
comicsCenarioSelect.addEventListener('change', () => {
|
||||||
@@ -4945,18 +4978,14 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
currentComicsCharDescEnglish = scriptData.character_description;
|
currentComicsCharDescEnglish = scriptData.character_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Gerar cada quadrinho sequencialmente
|
// 2. Gerar quadrinhos em PARALELO com tracking de progresso em tempo real
|
||||||
if (genMode !== 'extend') {
|
if (genMode !== 'extend') {
|
||||||
generatedPanelsData = [];
|
generatedPanelsData = [];
|
||||||
}
|
}
|
||||||
const totalPanels = scriptData.panels.length;
|
const totalPanels = scriptData.panels.length;
|
||||||
|
let completedCount = 0;
|
||||||
|
|
||||||
for (let i = 0; i < totalPanels; i++) {
|
const panelPromises = scriptData.panels.map(async (panel, idx) => {
|
||||||
const panel = scriptData.panels[i];
|
|
||||||
const progressPercent = 15 + Math.floor((i / totalPanels) * 80);
|
|
||||||
comicsLoaderProgress.style.width = `${progressPercent}%`;
|
|
||||||
comicsLoaderText.textContent = `Ilustrando quadrinho ${i + 1} de ${totalPanels}...`;
|
|
||||||
|
|
||||||
// Injetamos a proporção e estilo de forma reforçada no prompt
|
// Injetamos a proporção e estilo de forma reforçada no prompt
|
||||||
const fullPrompt = `${panel.image_prompt}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
const fullPrompt = `${panel.image_prompt}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
||||||
|
|
||||||
@@ -4968,65 +4997,43 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
|
|
||||||
if (!frameResp.ok) {
|
if (!frameResp.ok) {
|
||||||
const err = await frameResp.json();
|
const err = await frameResp.json();
|
||||||
throw new Error(`Erro no quadrinho ${i + 1}: ${err.error}`);
|
throw new Error(`Erro no quadrinho ${panel.panel_number}: ${err.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const frameData = await frameResp.json();
|
const frameData = await frameResp.json();
|
||||||
generatedPanelsData.push({
|
completedCount++;
|
||||||
|
const progressPercent = 15 + Math.floor((completedCount / totalPanels) * 80);
|
||||||
|
if (comicsLoaderProgress) comicsLoaderProgress.style.width = `${progressPercent}%`;
|
||||||
|
if (comicsLoaderText) comicsLoaderText.textContent = `⚡ Ilustrando em paralelo: ${completedCount}/${totalPanels} quadros concluídos...`;
|
||||||
|
|
||||||
|
return {
|
||||||
panel_number: panel.panel_number,
|
panel_number: panel.panel_number,
|
||||||
imageUrl: frameData.imageUrl,
|
imageUrl: frameData.imageUrl,
|
||||||
dialogue: panel.dialogue || '',
|
dialogue: panel.dialogue || '',
|
||||||
image_prompt: panel.image_prompt || ''
|
image_prompt: panel.image_prompt || ''
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const newPanels = await Promise.all(panelPromises);
|
||||||
|
|
||||||
|
// Ordena por panel_number para garantir a sequência correta
|
||||||
|
newPanels.sort((a, b) => a.panel_number - b.panel_number);
|
||||||
|
|
||||||
|
if (genMode === 'extend') {
|
||||||
|
generatedPanelsData = generatedPanelsData.concat(newPanels);
|
||||||
|
} else {
|
||||||
|
generatedPanelsData = newPanels;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renderizar quadrinhos na tela (todos, incluindo os anteriores caso tenha sido extensão)
|
// Atualizar cabeçalho e metadados
|
||||||
comicsResultTitle.textContent = scriptData.title || comicsResultTitle.textContent || 'Fábrica de Quadrinhos Pedagog';
|
comicsResultTitle.textContent = scriptData.title || comicsResultTitle.textContent || 'Fábrica de Quadrinhos Pedagog';
|
||||||
comicsGridContainer.innerHTML = '';
|
const metaPanels = document.getElementById('comicsMetaPanels');
|
||||||
|
if (metaPanels) metaPanels.textContent = `🖼️ ${generatedPanelsData.length} Quadros`;
|
||||||
|
const metaRatio = document.getElementById('comicsMetaRatio');
|
||||||
|
if (metaRatio) metaRatio.textContent = `📐 ${selectedComicsRatio} ${selectedComicsRatio === '16:9' ? 'HD' : 'Retrô'}`;
|
||||||
|
|
||||||
generatedPanelsData.forEach((panel, index) => {
|
// Renderizar Storyboard Interativo
|
||||||
const panelCard = document.createElement('div');
|
renderComicsStoryboard();
|
||||||
panelCard.className = 'comic-panel-card';
|
|
||||||
|
|
||||||
const imgContainer = document.createElement('div');
|
|
||||||
imgContainer.className = `comic-panel-image-container ratio-${selectedComicsRatio.replace(':', '-')}`;
|
|
||||||
|
|
||||||
const img = document.createElement('img');
|
|
||||||
img.className = 'comic-panel-image';
|
|
||||||
img.src = panel.imageUrl || panel.image_url;
|
|
||||||
img.alt = `Quadro ${panel.panel_number}`;
|
|
||||||
imgContainer.appendChild(img);
|
|
||||||
|
|
||||||
// Selo do número
|
|
||||||
const badge = document.createElement('div');
|
|
||||||
badge.className = 'comic-panel-number-badge';
|
|
||||||
badge.textContent = panel.panel_number;
|
|
||||||
imgContainer.appendChild(badge);
|
|
||||||
|
|
||||||
panelCard.appendChild(imgContainer);
|
|
||||||
|
|
||||||
const caption = document.createElement('div');
|
|
||||||
caption.className = 'comic-caption-text';
|
|
||||||
caption.style.cssText = 'padding: 10px 14px; background: var(--bg-primary); border-top: 1px solid var(--border-light);';
|
|
||||||
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.className = 'comic-caption-input';
|
|
||||||
textarea.value = panel.dialogue || '';
|
|
||||||
textarea.placeholder = 'Digite a fala ou legenda do quadrinho...';
|
|
||||||
textarea.style.cssText = 'width: 100%; border: 1px solid var(--border-light); background: var(--bg-secondary); color: var(--text-primary); border-radius: 6px; padding: 8px; font-family: inherit; font-size: 0.85rem; resize: vertical; box-sizing: border-box; min-height: 50px;';
|
|
||||||
|
|
||||||
textarea.addEventListener('input', (e) => {
|
|
||||||
panel.dialogue = e.target.value;
|
|
||||||
if (generatedPanelsData[index]) {
|
|
||||||
generatedPanelsData[index].dialogue = e.target.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
caption.appendChild(textarea);
|
|
||||||
panelCard.appendChild(caption);
|
|
||||||
|
|
||||||
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
|
// 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) {
|
if (genMode === 'new_story' && currentComicsProjectId) {
|
||||||
@@ -5053,6 +5060,336 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- RENDERIZADOR DO STORYBOARD PROFISSIONAL DOS QUADRINHOS ---
|
||||||
|
const renderComicsStoryboard = () => {
|
||||||
|
if (!comicsGridContainer) return;
|
||||||
|
comicsGridContainer.innerHTML = '';
|
||||||
|
|
||||||
|
generatedPanelsData.forEach((panel, index) => {
|
||||||
|
const panelCard = document.createElement('div');
|
||||||
|
panelCard.className = 'comic-panel-card';
|
||||||
|
panelCard.dataset.index = index;
|
||||||
|
|
||||||
|
const imgContainer = document.createElement('div');
|
||||||
|
imgContainer.className = `comic-panel-image-container ratio-${selectedComicsRatio.replace(':', '-')}`;
|
||||||
|
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.className = 'comic-panel-image';
|
||||||
|
img.src = panel.imageUrl || panel.image_url;
|
||||||
|
img.alt = `Quadro ${panel.panel_number}`;
|
||||||
|
img.loading = 'lazy';
|
||||||
|
imgContainer.appendChild(img);
|
||||||
|
|
||||||
|
// Selo de número do quadrinho
|
||||||
|
const badge = document.createElement('div');
|
||||||
|
badge.className = 'comic-panel-number-badge';
|
||||||
|
badge.textContent = panel.panel_number;
|
||||||
|
imgContainer.appendChild(badge);
|
||||||
|
|
||||||
|
// Barra de Ações Rápidas por Quadro (Mobile & Desktop)
|
||||||
|
const actionsOverlay = document.createElement('div');
|
||||||
|
actionsOverlay.className = 'comic-panel-overlay-actions';
|
||||||
|
|
||||||
|
// Botão 🔍 Zoom
|
||||||
|
const btnZoom = document.createElement('button');
|
||||||
|
btnZoom.type = 'button';
|
||||||
|
btnZoom.className = 'btn-comic-action';
|
||||||
|
btnZoom.title = 'Ampliar imagem';
|
||||||
|
btnZoom.innerHTML = '🔍';
|
||||||
|
btnZoom.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openComicsPresentation(index);
|
||||||
|
});
|
||||||
|
actionsOverlay.appendChild(btnZoom);
|
||||||
|
|
||||||
|
// Botão 🔄 Regenerar Imagem deste Quadro
|
||||||
|
const btnRegen = document.createElement('button');
|
||||||
|
btnRegen.type = 'button';
|
||||||
|
btnRegen.className = 'btn-comic-action';
|
||||||
|
btnRegen.title = 'Regenerar apenas este quadrinho';
|
||||||
|
btnRegen.innerHTML = '🔄';
|
||||||
|
btnRegen.addEventListener('click', async (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await regenerateSinglePanel(index, panelCard, img, btnRegen);
|
||||||
|
});
|
||||||
|
actionsOverlay.appendChild(btnRegen);
|
||||||
|
|
||||||
|
// Botão ✏️ Ver/Editar Prompt
|
||||||
|
const btnPromptToggle = document.createElement('button');
|
||||||
|
btnPromptToggle.type = 'button';
|
||||||
|
btnPromptToggle.className = 'btn-comic-action';
|
||||||
|
btnPromptToggle.title = 'Editar prompt visual';
|
||||||
|
btnPromptToggle.innerHTML = '✏️';
|
||||||
|
actionsOverlay.appendChild(btnPromptToggle);
|
||||||
|
|
||||||
|
// Botão 💾 Baixar Imagem Individual
|
||||||
|
const btnDownloadImg = document.createElement('button');
|
||||||
|
btnDownloadImg.type = 'button';
|
||||||
|
btnDownloadImg.className = 'btn-comic-action';
|
||||||
|
btnDownloadImg.title = 'Baixar esta imagem PNG';
|
||||||
|
btnDownloadImg.innerHTML = '💾';
|
||||||
|
btnDownloadImg.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = panel.imageUrl || panel.image_url;
|
||||||
|
a.download = `quadrinho_${panel.panel_number}.png`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
});
|
||||||
|
actionsOverlay.appendChild(btnDownloadImg);
|
||||||
|
|
||||||
|
imgContainer.appendChild(actionsOverlay);
|
||||||
|
panelCard.appendChild(imgContainer);
|
||||||
|
|
||||||
|
// Área de Legenda / Diálogo
|
||||||
|
const caption = document.createElement('div');
|
||||||
|
caption.className = 'comic-caption-text';
|
||||||
|
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.className = 'comic-caption-input';
|
||||||
|
textarea.value = panel.dialogue || '';
|
||||||
|
textarea.placeholder = 'Digite a fala ou legenda do quadrinho...';
|
||||||
|
textarea.style.cssText = 'width: 100%; border: 1px solid var(--border-light); background: var(--bg-secondary); color: var(--text-primary); border-radius: 6px; padding: 8px; font-family: inherit; font-size: 0.85rem; resize: vertical; box-sizing: border-box; min-height: 48px;';
|
||||||
|
|
||||||
|
textarea.addEventListener('input', (e) => {
|
||||||
|
panel.dialogue = e.target.value;
|
||||||
|
if (generatedPanelsData[index]) {
|
||||||
|
generatedPanelsData[index].dialogue = e.target.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
caption.appendChild(textarea);
|
||||||
|
|
||||||
|
// Mini-Editor de Prompt Visual (Oculto por padrão, ativado no botão ✏️)
|
||||||
|
const promptBox = document.createElement('div');
|
||||||
|
promptBox.className = 'comic-prompt-details';
|
||||||
|
const promptInput = document.createElement('textarea');
|
||||||
|
promptInput.style.cssText = 'width: 100%; font-size: 0.72rem; padding: 4px; background: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-light); border-radius: 4px; resize: vertical; min-height: 40px; box-sizing: border-box;';
|
||||||
|
promptInput.value = panel.image_prompt || '';
|
||||||
|
promptInput.addEventListener('input', (e) => {
|
||||||
|
panel.image_prompt = e.target.value;
|
||||||
|
});
|
||||||
|
promptBox.appendChild(document.createTextNode('Prompt Visual: '));
|
||||||
|
promptBox.appendChild(promptInput);
|
||||||
|
caption.appendChild(promptBox);
|
||||||
|
|
||||||
|
btnPromptToggle.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
promptBox.style.display = promptBox.style.display === 'block' ? 'none' : 'block';
|
||||||
|
});
|
||||||
|
|
||||||
|
panelCard.appendChild(caption);
|
||||||
|
comicsGridContainer.appendChild(panelCard);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Regenerar um único painel
|
||||||
|
const regenerateSinglePanel = async (index, panelCard, imgElement, btnRegen) => {
|
||||||
|
const panel = generatedPanelsData[index];
|
||||||
|
if (!panel) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
btnRegen.disabled = true;
|
||||||
|
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'}`;
|
||||||
|
|
||||||
|
const resp = await fetch('/api/comics/generate-frame', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ prompt: fullPrompt })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resp.ok) {
|
||||||
|
const err = await resp.json();
|
||||||
|
throw new Error(err.error || 'Erro na regeneração');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await resp.json();
|
||||||
|
panel.imageUrl = data.imageUrl;
|
||||||
|
imgElement.src = data.imageUrl + '?v=' + Date.now();
|
||||||
|
imgElement.style.opacity = '1';
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erro ao regenerar painel:', err);
|
||||||
|
await showCustomAlert('Erro', 'Não foi possível regenerar o quadrinho: ' + err.message);
|
||||||
|
imgElement.style.opacity = '1';
|
||||||
|
} finally {
|
||||||
|
btnRegen.disabled = false;
|
||||||
|
btnRegen.innerHTML = '🔄';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- EXPORTAR TODAS AS IMAGENS EM ZIP ---
|
||||||
|
const btnExportComicsZIP = document.getElementById('btnExportComicsZIP');
|
||||||
|
if (btnExportComicsZIP) {
|
||||||
|
btnExportComicsZIP.addEventListener('click', async () => {
|
||||||
|
if (!generatedPanelsData || generatedPanelsData.length === 0) {
|
||||||
|
await showCustomAlert('Aviso', 'Nenhum quadrinho gerado para exportar.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
btnExportComicsZIP.disabled = true;
|
||||||
|
btnExportComicsZIP.textContent = '⏳ Criando ZIP...';
|
||||||
|
|
||||||
|
if (typeof JSZip === 'undefined') {
|
||||||
|
throw new Error('Biblioteca JSZip não carregada.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const zip = new JSZip();
|
||||||
|
const folder = zip.folder('quadrinhos');
|
||||||
|
const title = (comicsResultTitle?.textContent || 'Historia_Quadrinhos').replace(/[^a-zA-Z0-9_-]/g, '_');
|
||||||
|
|
||||||
|
let roteiroTxt = `HISTÓRIA EM QUADRINHOS: ${comicsResultTitle?.textContent || 'Fábrica de Quadrinhos'}\n`;
|
||||||
|
roteiroTxt += `Criado com PedagogIA em ${new Date().toLocaleDateString('pt-BR')}\n\n`;
|
||||||
|
|
||||||
|
for (let i = 0; i < generatedPanelsData.length; i++) {
|
||||||
|
const panel = generatedPanelsData[i];
|
||||||
|
const imgUrl = panel.imageUrl || panel.image_url;
|
||||||
|
roteiroTxt += `[QUADRO ${panel.panel_number}]\nFala/Legenda: ${panel.dialogue || '(Sem fala)'}\nPrompt Visual: ${panel.image_prompt || ''}\n\n`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(imgUrl);
|
||||||
|
const blob = await resp.blob();
|
||||||
|
folder.file(`quadrinho_${panel.panel_number}.jpg`, blob);
|
||||||
|
} catch (fetchErr) {
|
||||||
|
console.warn(`Erro ao incluir imagem ${panel.panel_number} no ZIP:`, fetchErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
folder.file('roteiro.txt', roteiroTxt);
|
||||||
|
|
||||||
|
const content = await zip.generateAsync({ type: 'blob' });
|
||||||
|
const downloadUrl = URL.createObjectURL(content);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = downloadUrl;
|
||||||
|
a.download = `${title}_pedagog.zip`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
URL.revokeObjectURL(downloadUrl);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erro ao gerar ZIP:', err);
|
||||||
|
await showCustomAlert('Erro', 'Não foi possível gerar o ZIP: ' + err.message);
|
||||||
|
} finally {
|
||||||
|
btnExportComicsZIP.disabled = false;
|
||||||
|
btnExportComicsZIP.innerHTML = '<span>📦 Baixar Todas as Imagens (ZIP)</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MODO APRESENTAÇÃO EM TELA CHEIA (LEITURA INFANTIL / MOBILE) ---
|
||||||
|
const comicsPresModal = document.getElementById('comicsPresentationModal');
|
||||||
|
const comicsPresTitle = document.getElementById('comicsPresTitle');
|
||||||
|
const comicsPresImage = document.getElementById('comicsPresImage');
|
||||||
|
const comicsPresCaption = document.getElementById('comicsPresCaption');
|
||||||
|
const comicsPresCounter = document.getElementById('comicsPresCounter');
|
||||||
|
const btnPresPrev = document.getElementById('btnPresPrev');
|
||||||
|
const btnPresNext = document.getElementById('btnPresNext');
|
||||||
|
const btnCloseComicsPresModal = document.getElementById('btnCloseComicsPresModal');
|
||||||
|
const btnComicsPresentationMode = document.getElementById('btnComicsPresentationMode');
|
||||||
|
|
||||||
|
let presCurrentIndex = 0;
|
||||||
|
|
||||||
|
const openComicsPresentation = (index = 0) => {
|
||||||
|
if (!generatedPanelsData || generatedPanelsData.length === 0) return;
|
||||||
|
presCurrentIndex = Math.max(0, Math.min(index, generatedPanelsData.length - 1));
|
||||||
|
if (comicsPresModal) comicsPresModal.style.display = 'flex';
|
||||||
|
updateComicsPresentationView();
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeComicsPresentation = () => {
|
||||||
|
if (comicsPresModal) comicsPresModal.style.display = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateComicsPresentationView = () => {
|
||||||
|
if (!generatedPanelsData || generatedPanelsData.length === 0) return;
|
||||||
|
const panel = generatedPanelsData[presCurrentIndex];
|
||||||
|
if (!panel) return;
|
||||||
|
|
||||||
|
if (comicsPresTitle) comicsPresTitle.textContent = comicsResultTitle?.textContent || 'Fábrica de Quadrinhos';
|
||||||
|
if (comicsPresCounter) comicsPresCounter.textContent = `${presCurrentIndex + 1} / ${generatedPanelsData.length}`;
|
||||||
|
if (comicsPresImage) comicsPresImage.src = panel.imageUrl || panel.image_url;
|
||||||
|
if (comicsPresCaption) comicsPresCaption.textContent = panel.dialogue || '(História silenciosa)';
|
||||||
|
|
||||||
|
if (btnPresPrev) btnPresPrev.disabled = presCurrentIndex === 0;
|
||||||
|
if (btnPresNext) btnPresNext.disabled = presCurrentIndex === generatedPanelsData.length - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (btnComicsPresentationMode) {
|
||||||
|
btnComicsPresentationMode.addEventListener('click', () => {
|
||||||
|
openComicsPresentation(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnPresPrev) {
|
||||||
|
btnPresPrev.addEventListener('click', () => {
|
||||||
|
if (presCurrentIndex > 0) {
|
||||||
|
presCurrentIndex--;
|
||||||
|
updateComicsPresentationView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnPresNext) {
|
||||||
|
btnPresNext.addEventListener('click', () => {
|
||||||
|
if (presCurrentIndex < generatedPanelsData.length - 1) {
|
||||||
|
presCurrentIndex++;
|
||||||
|
updateComicsPresentationView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnCloseComicsPresModal) {
|
||||||
|
btnCloseComicsPresModal.addEventListener('click', closeComicsPresentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teclado (setas) para o modo apresentação
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (comicsPresModal && comicsPresModal.style.display === 'flex') {
|
||||||
|
if (e.key === 'ArrowRight' || e.key === 'Space') {
|
||||||
|
if (presCurrentIndex < generatedPanelsData.length - 1) {
|
||||||
|
presCurrentIndex++;
|
||||||
|
updateComicsPresentationView();
|
||||||
|
}
|
||||||
|
} else if (e.key === 'ArrowLeft') {
|
||||||
|
if (presCurrentIndex > 0) {
|
||||||
|
presCurrentIndex--;
|
||||||
|
updateComicsPresentationView();
|
||||||
|
}
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
closeComicsPresentation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Touch Swipe para smartphones no modo apresentação
|
||||||
|
let touchStartX = 0;
|
||||||
|
if (comicsPresModal) {
|
||||||
|
comicsPresModal.addEventListener('touchstart', (e) => {
|
||||||
|
touchStartX = e.changedTouches[0].screenX;
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
comicsPresModal.addEventListener('touchend', (e) => {
|
||||||
|
const touchEndX = e.changedTouches[0].screenX;
|
||||||
|
const diff = touchEndX - touchStartX;
|
||||||
|
if (Math.abs(diff) > 50) {
|
||||||
|
if (diff < 0 && presCurrentIndex < generatedPanelsData.length - 1) {
|
||||||
|
// Swipe para a esquerda -> Próximo
|
||||||
|
presCurrentIndex++;
|
||||||
|
updateComicsPresentationView();
|
||||||
|
} else if (diff > 0 && presCurrentIndex > 0) {
|
||||||
|
// Swipe para a direita -> Anterior
|
||||||
|
presCurrentIndex--;
|
||||||
|
updateComicsPresentationView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
// Exportar PDF Retrato (1 por página)
|
// Exportar PDF Retrato (1 por página)
|
||||||
if (btnExportComicsPDFPortrait) {
|
if (btnExportComicsPDFPortrait) {
|
||||||
btnExportComicsPDFPortrait.addEventListener('click', async () => {
|
btnExportComicsPDFPortrait.addEventListener('click', async () => {
|
||||||
|
|||||||
+75
-20
@@ -1400,9 +1400,10 @@
|
|||||||
|
|
||||||
<!-- PERSONAGENS -->
|
<!-- PERSONAGENS -->
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; display: flex; justify-content: space-between; align-items: center;">
|
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 6px;">
|
||||||
<span>5. Personagens da História</span>
|
<span>5. Personagens da História</span>
|
||||||
<div style="display: flex; gap: 8px;">
|
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
|
||||||
|
<button type="button" id="btnImportTurmaChars" class="btn-music-option" style="padding: 3px 8px; font-size: 0.72rem; border-color: #3b82f6; color: #60a5fa; background: rgba(59, 130, 246, 0.08); margin: 0;" title="Carregar crianças da turma atual como personagens">+ 🏫 Importar da Turma</button>
|
||||||
<button type="button" id="btnAddHumanChar" class="btn-music-option" style="padding: 3px 8px; font-size: 0.72rem; border-color: var(--brand-pink); color: var(--brand-pink); background: rgba(219, 39, 119, 0.05); margin: 0;">+ 🧒 Humano</button>
|
<button type="button" id="btnAddHumanChar" class="btn-music-option" style="padding: 3px 8px; font-size: 0.72rem; border-color: var(--brand-pink); color: var(--brand-pink); background: rgba(219, 39, 119, 0.05); margin: 0;">+ 🧒 Humano</button>
|
||||||
<button type="button" id="btnAddAnimalChar" class="btn-music-option" style="padding: 3px 8px; font-size: 0.72rem; border-color: var(--brand-pink); color: var(--brand-pink); background: rgba(219, 39, 119, 0.05); margin: 0;">+ 🐾 Animal</button>
|
<button type="button" id="btnAddAnimalChar" class="btn-music-option" style="padding: 3px 8px; font-size: 0.72rem; border-color: var(--brand-pink); color: var(--brand-pink); background: rgba(219, 39, 119, 0.05); margin: 0;">+ 🐾 Animal</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1432,45 +1433,66 @@
|
|||||||
|
|
||||||
<!-- BOTÃO DE AÇÃO -->
|
<!-- BOTÃO DE AÇÃO -->
|
||||||
<button id="btnGenerateComics" style="width: 100%; padding: 12px; background: var(--brand-pink, #db2777); color: white; border: none; border-radius: 10px; font-weight: 700; font-size: 1rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 12px rgba(219, 39, 119, 0.3); transition: all 0.2s;">
|
<button id="btnGenerateComics" style="width: 100%; padding: 12px; background: var(--brand-pink, #db2777); color: white; border: none; border-radius: 10px; font-weight: 700; font-size: 1rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 12px rgba(219, 39, 119, 0.3); transition: all 0.2s;">
|
||||||
<span>🎨 Criar Quadrinhos Pedagógicos</span>
|
<span>🎨 Criar Quadrinhos Pedagógicos (Geração Paralela ⚡)</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- LOADING DE QUADRINHOS -->
|
<!-- LOADING DE QUADRINHOS -->
|
||||||
<div id="comicsGenerateLoader" style="display: none; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 30px; background: var(--bg-secondary); border-radius: 12px; border: 1px dashed var(--border-light);">
|
<div id="comicsGenerateLoader" style="display: none; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 30px; background: var(--bg-secondary); border-radius: 12px; border: 1px dashed var(--border-light);">
|
||||||
<div class="media-spinner" style="border-top-color: var(--brand-pink, #db2777);"></div>
|
<div class="media-spinner" style="border-top-color: var(--brand-pink, #db2777);"></div>
|
||||||
<p id="comicsLoaderText" style="color: var(--text-primary); font-weight: 600; font-size: 0.95rem; margin: 0; text-align: center;">Compondo a história pedagógica...</p>
|
<p id="comicsLoaderText" style="color: var(--text-primary); font-weight: 600; font-size: 0.95rem; margin: 0; text-align: center;">Compondo a história pedagógica...</p>
|
||||||
<div style="width: 100%; max-width: 250px; height: 6px; background: var(--border-light); border-radius: 3px; overflow: hidden;">
|
<div style="width: 100%; max-width: 320px; height: 8px; background: var(--border-light); border-radius: 4px; overflow: hidden;">
|
||||||
<div id="comicsLoaderProgress" style="width: 0%; height: 100%; background: var(--brand-pink, #db2777); transition: width 0.4s ease;"></div>
|
<div id="comicsLoaderProgress" style="width: 0%; height: 100%; background: var(--brand-pink, #db2777); transition: width 0.3s ease;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<span id="comicsLoaderSubtext" style="font-size: 0.78rem; color: var(--text-muted);">Processando quadros em paralelo de alta performance...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- RESULTADOS DA HISTÓRIA -->
|
<!-- RESULTADOS DA HISTÓRIA (STORYBOARD STUDIO) -->
|
||||||
<div id="comicsResultArea" style="display: none; flex-direction: column; gap: 20px;">
|
<div id="comicsResultArea" style="display: none; flex-direction: column; gap: 18px;">
|
||||||
|
|
||||||
<h4 id="comicsResultTitle" style="font-family: 'Outfit', sans-serif; font-size: 1.15rem; color: var(--text-primary); margin: 0; text-align: center; font-weight: 700;">Título da História</h4>
|
<!-- BARRA DE CABEÇALHO DO STORYBOARD -->
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 10px; background: var(--bg-secondary); padding: 14px 16px; border-radius: 10px; border: 1px solid var(--border-light);">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
|
||||||
|
<h4 id="comicsResultTitle" style="font-family: 'Outfit', sans-serif; font-size: 1.2rem; color: var(--text-primary); margin: 0; font-weight: 700;">Título da História</h4>
|
||||||
|
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
|
||||||
|
<button type="button" id="btnComicsPresentationMode" class="btn-music-option" style="border-color: #8b5cf6; color: #a78bfa; background: rgba(139, 92, 246, 0.1); font-weight: 600; display: flex; align-items: center; gap: 6px; padding: 6px 14px;">
|
||||||
|
<span>🎬 Apresentar em Tela Cheia</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnExportComicsZIP" class="btn-music-option" style="border-color: #3b82f6; color: #60a5fa; background: rgba(59, 130, 246, 0.1); font-weight: 600; display: flex; align-items: center; gap: 6px; padding: 6px 14px;">
|
||||||
|
<span>📦 Baixar Todas as Imagens (ZIP)</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- CONTÊINER DOS QUADRINHOS -->
|
<div id="comicsResultMeta" style="font-size: 0.78rem; color: var(--text-muted); display: flex; gap: 14px; flex-wrap: wrap; border-top: 1px dashed var(--border-light); padding-top: 8px;">
|
||||||
<div id="comicsGridContainer" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px;">
|
<span>🤖 Criado por: <strong>PedagogIA</strong></span>
|
||||||
<!-- Gerado dinamicamente: quadrinhos com as imagens e balões de fala correspondentes -->
|
<span id="comicsMetaPanels">🖼️ 5 Quadros</span>
|
||||||
|
<span id="comicsMetaRatio">📐 16:9 HD</span>
|
||||||
|
<span>✨ Dica: Você pode editar o texto dos balões ou regenerar qualquer quadrinho individualmente abaixo!</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- OPÇÕES DE EXPORTAÇÃO -->
|
<!-- CONTÊINER DOS QUADRINHOS EM STORYBOARD -->
|
||||||
|
<div id="comicsGridContainer" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 18px;">
|
||||||
|
<!-- Gerado dinamicamente: cards de quadrinhos interativos -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- OPÇÕES DE EXPORTAÇÃO E VÍDEO -->
|
||||||
<div style="background: var(--bg-secondary); padding: 16px; border-radius: 12px; border: 1px solid var(--border-light); display: flex; flex-direction: column; gap: 14px;">
|
<div style="background: var(--bg-secondary); padding: 16px; border-radius: 12px; border: 1px solid var(--border-light); display: flex; flex-direction: column; gap: 14px;">
|
||||||
|
|
||||||
<h5 style="margin: 0; font-size: 0.9rem; font-weight: 700; text-transform: uppercase; color: var(--text-secondary); letter-spacing: 0.5px;">📥 Opções de Exportação</h5>
|
<h5 style="margin: 0; font-size: 0.9rem; font-weight: 700; text-transform: uppercase; color: var(--text-secondary); letter-spacing: 0.5px;">📥 Mais Formatos de Exportação</h5>
|
||||||
|
|
||||||
<!-- DOWNLOAD DE PDF -->
|
<!-- DOWNLOAD DE PDF -->
|
||||||
<div style="display: flex; flex-direction: column; gap: 6px;">
|
<div style="display: flex; flex-direction: column; gap: 6px;">
|
||||||
<span style="font-size: 0.8rem; font-weight: 600; color: var(--text-primary);">Salvar como Documento PDF:</span>
|
<span style="font-size: 0.8rem; font-weight: 600; color: var(--text-primary);">Documento PDF para Impressão ou Registro Pedagógico:</span>
|
||||||
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;">
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;">
|
||||||
<button id="btnExportComicsPDFPortrait" class="btn-music-option" style="border-color: #10a37f; color: #10a37f; background: rgba(16, 163, 127, 0.05);">📄 PDF (1 por página - Retrato)</button>
|
<button id="btnExportComicsPDFPortrait" class="btn-music-option" style="border-color: #10a37f; color: #10a37f; background: rgba(16, 163, 127, 0.05); font-weight: 600;">📄 PDF (1 por página - Retrato A4)</button>
|
||||||
<button id="btnExportComicsPDFLandscape" class="btn-music-option" style="border-color: #10a37f; color: #10a37f; background: rgba(16, 163, 127, 0.05);">📄 PDF (2 por página - Paisagem)</button>
|
<button id="btnExportComicsPDFLandscape" class="btn-music-option" style="border-color: #10a37f; color: #10a37f; background: rgba(16, 163, 127, 0.05); font-weight: 600;">📄 PDF (2 por página - Álbum Paisagem)</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- APRESENTAÇÃO EM VÍDEO -->
|
<!-- APRESENTAÇÃO EM VÍDEO -->
|
||||||
<div style="display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--border-light); padding-top: 14px;">
|
<div style="display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--border-light); padding-top: 14px;">
|
||||||
<span style="font-size: 0.8rem; font-weight: 600; color: var(--text-primary);">Criar Apresentação de Vídeo Animada:</span>
|
<span style="font-size: 0.8rem; font-weight: 600; color: var(--text-primary);">Criar Apresentação de Vídeo Animada com Música:</span>
|
||||||
|
|
||||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
|
||||||
<div class="settings-group" style="gap: 4px;">
|
<div class="settings-group" style="gap: 4px;">
|
||||||
@@ -1492,7 +1514,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="btnCompileComicsVideo" style="padding: 10px; background: var(--brand-pink, #db2777); color: white; border: none; border-radius: 8px; font-weight: 600; font-size: 0.88rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 6px; transition: all 0.2s;">
|
<button id="btnCompileComicsVideo" style="padding: 10px; background: var(--brand-pink, #db2777); color: white; border: none; border-radius: 8px; font-weight: 600; font-size: 0.88rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 6px; transition: all 0.2s;">
|
||||||
<span>🎬 Gerar Apresentação de Vídeo (MP4)</span>
|
<span>🎬 Compilar Apresentação de Vídeo (MP4)</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Spinner do Vídeo -->
|
<!-- Spinner do Vídeo -->
|
||||||
@@ -2036,7 +2058,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bibliotecas externas para renderizar markdown e blocos de código -->
|
<!-- Modal: Apresentação em Tela Cheia dos Quadrinhos (Modo Leitura Infantil / Lousa / Mobile) -->
|
||||||
|
<div id="comicsPresentationModal" class="comics-presentation-modal" style="display: none;">
|
||||||
|
<div class="comics-pres-header">
|
||||||
|
<div style="display: flex; align-items: center; gap: 10px;">
|
||||||
|
<span style="font-size: 1.3rem;">📖</span>
|
||||||
|
<h3 id="comicsPresTitle" style="margin: 0; font-family: 'Outfit', sans-serif; font-size: 1.1rem; color: #fff;">História em Quadrinhos</h3>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px;">
|
||||||
|
<span id="comicsPresCounter" style="background: rgba(255,255,255,0.15); padding: 4px 12px; border-radius: 20px; font-size: 0.85rem; font-weight: 700; color: #fef08a;">1 / 5</span>
|
||||||
|
<button type="button" id="btnCloseComicsPresModal" style="background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: white; width: 36px; height: 36px; border-radius: 50%; cursor: pointer; font-size: 1.2rem; display: flex; align-items: center; justify-content: center; transition: all 0.2s;">✕</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="comics-pres-stage">
|
||||||
|
<div class="comics-pres-card">
|
||||||
|
<img id="comicsPresImage" class="comics-pres-img" src="" alt="Quadrinho em Apresentação">
|
||||||
|
<div id="comicsPresCaption" class="comics-pres-caption">
|
||||||
|
Falas do quadrinho aparecerão aqui...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="comics-pres-nav">
|
||||||
|
<button type="button" id="btnPresPrev" class="btn-pres-nav">
|
||||||
|
<span>⬅️ Anterior</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnPresNext" class="btn-pres-nav" style="background: var(--brand-pink, #db2777); font-weight: 700;">
|
||||||
|
<span>Próximo ➡️</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bibliotecas externas para renderizar markdown, PDFs e compactação ZIP -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
|
|||||||
+219
-21
@@ -3358,22 +3358,22 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
FÁBRICA DE QUADRINHOS
|
FÁBRICA DE QUADRINHOS — STORYBOARD STUDIO & PRESENTATION MODE
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
.comic-panel-card {
|
.comic-panel-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 3px solid #1e293b;
|
border: 3px solid #1e293b;
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
box-shadow: 4px 4px 0px #1e293b;
|
box-shadow: 4px 4px 0px #1e293b;
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
transition: transform 0.2s, box-shadow 0.2s;
|
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
.comic-panel-card:hover {
|
.comic-panel-card:hover {
|
||||||
transform: translate(-2px, -2px);
|
transform: translateY(-3px);
|
||||||
box-shadow: 6px 6px 0px #1e293b;
|
box-shadow: 6px 8px 0px #1e293b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comic-panel-image-container {
|
.comic-panel-image-container {
|
||||||
@@ -3396,51 +3396,103 @@ code {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.comic-panel-card:hover .comic-panel-image {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comic-panel-overlay-actions {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
z-index: 15;
|
||||||
|
opacity: 0.9;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.comic-panel-card:hover .comic-panel-overlay-actions {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-comic-action {
|
||||||
|
background: rgba(15, 23, 42, 0.85);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 4px 7px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
transition: background 0.2s, transform 0.1s;
|
||||||
|
}
|
||||||
|
.btn-comic-action:hover {
|
||||||
|
background: var(--brand-pink, #db2777);
|
||||||
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.comic-speech-bubble {
|
.comic-speech-bubble {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
bottom: 10px;
|
||||||
left: 8px;
|
left: 10px;
|
||||||
background: white;
|
right: 10px;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
border: 2px solid #1e293b;
|
border: 2px solid #1e293b;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
padding: 4px 8px;
|
padding: 6px 10px;
|
||||||
font-size: 0.72rem;
|
font-size: 0.78rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
max-width: 65%;
|
box-shadow: 2px 2px 0px rgba(30, 41, 59, 0.2);
|
||||||
box-shadow: 2px 2px 0px rgba(30, 41, 59, 0.15);
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comic-panel-number-badge {
|
.comic-panel-number-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
background: #fef08a; /* Amarelo claro */
|
background: #fef08a; /* Amarelo clássico gibi */
|
||||||
color: #1e293b;
|
color: #1e293b;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-size: 0.8rem;
|
font-size: 0.82rem;
|
||||||
width: 20px;
|
padding: 2px 8px;
|
||||||
height: 20px;
|
border-radius: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 2px solid #1e293b;
|
border: 2px solid #1e293b;
|
||||||
border-radius: 50%;
|
|
||||||
z-index: 12;
|
z-index: 12;
|
||||||
box-shadow: 1px 1px 0px #1e293b;
|
box-shadow: 2px 2px 0px #1e293b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comic-caption-text {
|
.comic-caption-text {
|
||||||
padding: 10px 14px;
|
padding: 10px 12px;
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
border-top: 1px solid var(--border-light);
|
border-top: 1px solid var(--border-light);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comic-prompt-details {
|
||||||
|
display: none;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px dashed var(--border-light);
|
||||||
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-comics-qty.active, .btn-comics-bubbles.active, .btn-comics-ratio.active, .btn-comics-char.active {
|
.btn-comics-qty.active, .btn-comics-bubbles.active, .btn-comics-ratio.active, .btn-comics-char.active {
|
||||||
@@ -3448,7 +3500,113 @@ code {
|
|||||||
border-color: #db2777;
|
border-color: #db2777;
|
||||||
color: #f472b6;
|
color: #f472b6;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
box-shadow: 0 0 8px rgba(219, 39, 119, 0.2);
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
MODO APRESENTAÇÃO / LEITURA (FULLSCREEN STORYBOARD)
|
||||||
|
========================================================================== */
|
||||||
|
.comics-presentation-modal {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 10000;
|
||||||
|
background: rgba(10, 10, 15, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-header {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-stage {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 960px;
|
||||||
|
position: relative;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-card {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 70vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: #0f172a;
|
||||||
|
border: 4px solid #334155;
|
||||||
|
border-radius: 14px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8);
|
||||||
|
animation: comicFadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 55vh;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-caption {
|
||||||
|
width: 100%;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #0f172a;
|
||||||
|
padding: 16px 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 3px solid #1e293b;
|
||||||
|
font-family: 'Fredoka', 'Outfit', sans-serif;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pres-nav {
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 30px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.btn-pres-nav:hover:not(:disabled) {
|
||||||
|
background: var(--brand-pink, #db2777);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
.btn-pres-nav:disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes comicFadeIn {
|
||||||
|
from { opacity: 0; transform: scale(0.97); }
|
||||||
|
to { opacity: 1; transform: scale(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
@@ -3635,6 +3793,46 @@ code {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 8px 4px;
|
padding: 8px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Otimização Mobile para a Fábrica de Quadrinhos */
|
||||||
|
#comicsGridContainer {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
gap: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comic-panel-overlay-actions {
|
||||||
|
opacity: 1 !important;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-comic-action {
|
||||||
|
padding: 6px 9px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-presentation-modal {
|
||||||
|
padding: 12px 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-card {
|
||||||
|
max-height: 75vh !important;
|
||||||
|
border-width: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-img {
|
||||||
|
max-height: 46vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comics-pres-caption {
|
||||||
|
font-size: 0.98rem !important;
|
||||||
|
padding: 12px 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pres-nav {
|
||||||
|
padding: 8px 16px !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilos de progresso da IA (Chain of Thought/Etapas) */
|
/* Estilos de progresso da IA (Chain of Thought/Etapas) */
|
||||||
|
|||||||
Reference in New Issue
Block a user