Feature: Inventando Histórias profissional com 3-stage pipeline narrativa, vinculacao a turmas/alunos, ilustracoes por cena, leitor interativo, TTS narrado e exportacao PDF/TXT

This commit is contained in:
2026-08-31 11:25:33 +00:00
parent 3931af93db
commit 837762c1af
4 changed files with 1193 additions and 175 deletions
+530 -68
View File
@@ -7694,8 +7694,67 @@ window.toggleCustomAudio = (btn) => {
// ============================================================
// INVENTANDO HISTORIAS
// INVENTANDO HISTÓRIAS (ESTÚDIO PROFISSIONAL)
// ============================================================
const HISTORIA_TEMPLATES = {
arca: {
titulo: 'A Arca da Imaginação',
objeto: 'Arca Mágica de Madeira',
tema: 'As crianças encontram uma arca misteriosa no parquinho que se abre apenas quando todos dão as mãos e compartilham uma ideia criativa.',
estilo: 'Lúdico, Afetivo e Dialogado',
faixa: 'Crianças pequenas (4 anos a 5 anos e 11 meses)',
visual: 'Cartoon colorido e acolhedor'
},
horta: {
titulo: 'O Mistério da Horta Encantada',
objeto: 'Semente Brilhante de Girassol',
tema: 'A turma planta uma sementinha brilhante na horta da escola e descobre que ela cresce quando ouve canções de carinho e risadas.',
estilo: 'Contação Clássica com Suspense Leve',
faixa: 'Crianças pequenas (4 anos a 5 anos e 11 meses)',
visual: 'Aquarela suave infantil'
},
dinossauro: {
titulo: 'O Pequeno Dinossauro Amigo',
objeto: 'Pegada Colorida de Brinquedo',
tema: 'Um pequeno dinossauro verde e tímido aparece na hora do lanche procurando amigos para brincar de roda e desenhar.',
estilo: 'Lúdico, Afetivo e Dialogado',
faixa: 'Crianças bem pequenas (1 ano e 7 meses a 3 anos e 11 meses)',
visual: 'Cartoon colorido e acolhedor'
},
chuva: {
titulo: 'O Dia em que a Chuva Dançou',
objeto: 'Galochas Amarelas Saltitantes',
tema: 'Em um dia chuvoso, as gotas batem na janela fazendo música e convidam as crianças para uma dança aconchegante dentro da sala.',
estilo: 'Roda de Conversa Interativa',
faixa: 'Crianças pequenas (4 anos a 5 anos e 11 meses)',
visual: 'Aquarela suave infantil'
},
bolhas: {
titulo: 'O Reino Mágico das Bolhas de Sabão',
objeto: 'Varinha de Fazer Bolhas Gigantes',
tema: 'Cada bolha de sabão que as crianças assopram no pátio leva um desejo de abraço e alegria até as nuvens coloridas.',
estilo: 'Lúdico, Afetivo e Dialogado',
faixa: 'Bebês (0 a 1 ano e 6 meses)',
visual: 'Giz de cera e textura lúdica'
},
brinquedos: {
titulo: 'A Reunião Secreta dos Brinquedos',
objeto: 'Ursinho de Pano Guardião',
tema: 'Na hora do descanso, os brinquedos se organizam com muito capricho para esperar o acordar dos alunos com uma surpresa de blocos montados.',
estilo: 'Aventura e Descoberta na Escola',
faixa: 'Crianças pequenas (4 anos a 5 anos e 11 meses)',
visual: 'Livro infantil clássico e detalhado'
},
emocoes: {
titulo: 'O Monstrinho das Cores e dos Sentimentos',
objeto: 'Pote Brilhante das Emoções',
tema: 'Um monstrinho amigo muda de cor quando sente alegria (amarelo), calma (azul) ou saudade (rosa), ensinando a turma a expressar o que sente.',
estilo: 'Roda de Conversa Interativa',
faixa: 'Crianças pequenas (4 anos a 5 anos e 11 meses)',
visual: 'Cartoon colorido e acolhedor'
}
};
const barBtnHistoria = document.getElementById('barBtnHistoria');
const historiaModal = document.getElementById('historiaModal');
const btnCloseHistoriaModal = document.getElementById('btnCloseHistoriaModal');
@@ -7703,35 +7762,118 @@ window.toggleCustomAudio = (btn) => {
const historiaHistoryModal = document.getElementById('historiaHistoryModal');
const btnCloseHistoriaHistory = document.getElementById('btnCloseHistoriaHistory');
const historiaTemplateSelect = document.getElementById('historiaTemplateSelect');
const historiaTurmaSelect = document.getElementById('historiaTurmaSelect');
const historiaPersonagemInput = document.getElementById('historiaPersonagemInput');
const historiaObjetoMagicoInput = document.getElementById('historiaObjetoMagicoInput');
const historiaFaixaEtaria = document.getElementById('historiaFaixaEtaria');
const historiaParagrafos = document.getElementById('historiaParagrafos');
const historiaPersonagens = document.getElementById('historiaPersonagens');
const historiaEstilo = document.getElementById('historiaEstilo');
const historiaEstiloVisual = document.getElementById('historiaEstiloVisual');
const historiaTemaInput = document.getElementById('historiaTemaInput');
const btnGenerateHistoria = document.getElementById('btnGenerateHistoria');
const historiaGenerateLoader = document.getElementById('historiaGenerateLoader');
const historiaResultArea = document.getElementById('historiaResultArea');
const historiaTextOutput = document.getElementById('historiaTextOutput');
const btnCopyHistoria = document.getElementById('btnCopyHistoria');
const btnDownloadHistoriaPdf = document.getElementById('btnDownloadHistoriaPdf');
let currentHistoria = '';
// Elementos do Leitor e Visualizador
const historiaPlaceholderText = document.getElementById('historiaPlaceholderText');
const historiaActiveBox = document.getElementById('historiaActiveBox');
const historiaActiveTitle = document.getElementById('historiaActiveTitle');
const historiaActiveSubtitle = document.getElementById('historiaActiveSubtitle');
const historiaBnccBadge = document.getElementById('historiaBnccBadge');
if (barBtnHistoria) {
barBtnHistoria.addEventListener('click', () => {
historiaModal.style.display = 'flex';
historiaTemaInput.value = '';
historiaResultArea.style.display = 'none';
historiaGenerateLoader.style.display = 'none';
btnGenerateHistoria.disabled = false;
currentHistoria = '';
// Player de Áudio
const historiaAudioPlayerBox = document.getElementById('historiaAudioPlayerBox');
const historiaAudioPlayer = document.getElementById('historiaAudioPlayer');
const btnDownloadHistoriaAudio = document.getElementById('btnDownloadHistoriaAudio');
const historiaWaveBars = document.querySelectorAll('.wave-bar-historia');
// Abas
const historiaContentTabs = document.getElementById('historiaContentTabs');
const btnTabHistoriaCenas = document.getElementById('btnTabHistoriaCenas');
const btnTabHistoriaTexto = document.getElementById('btnTabHistoriaTexto');
const btnTabHistoriaAtividades = document.getElementById('btnTabHistoriaAtividades');
const historiaCenasView = document.getElementById('historiaCenasView');
const historiaTextoView = document.getElementById('historiaTextoView');
const historiaAtividadesView = document.getElementById('historiaAtividadesView');
// Cenas Interativas
const btnPrevCena = document.getElementById('btnPrevCena');
const btnNextCena = document.getElementById('btnNextCena');
const historiaCenaIndicator = document.getElementById('historiaCenaIndicator');
const historiaCenaTitle = document.getElementById('historiaCenaTitle');
const historiaCenaText = document.getElementById('historiaCenaText');
const historiaCenaImageContainer = document.getElementById('historiaCenaImageContainer');
const historiaCenaImg = document.getElementById('historiaCenaImg');
const btnGenerateSceneImage = document.getElementById('btnGenerateSceneImage');
// BNCC e Perguntas
const historiaBnccDesc = document.getElementById('historiaBnccDesc');
const historiaPerguntasList = document.getElementById('historiaPerguntasList');
// Ações
const btnCopyActiveHistoria = document.getElementById('btnCopyActiveHistoria');
const btnDownloadActiveHistoriaTxt = document.getElementById('btnDownloadActiveHistoriaTxt');
const btnSendHistoriaToMusic = document.getElementById('btnSendHistoriaToMusic');
const btnExportActiveHistoriaPdf = document.getElementById('btnExportActiveHistoriaPdf');
let currentHistoriaData = null;
let currentSceneIndex = 0;
// Carregar turmas no select de histórias
async function loadTurmasForHistoria() {
if (!historiaTurmaSelect) return;
try {
const res = await fetch('/api/turmas');
if (res.ok) {
const turmas = await res.json();
historiaTurmaSelect.innerHTML = '<option value="">-- Selecionar Turma --</option>';
turmas.forEach(t => {
const opt = document.createElement('option');
opt.value = t.id;
opt.textContent = `${t.nome} (${t.ano_letivo || '2026'})`;
historiaTurmaSelect.appendChild(opt);
});
}
} catch (e) {
console.warn('Erro ao carregar turmas para histórias:', e);
}
}
// Mudança de template
if (historiaTemplateSelect) {
historiaTemplateSelect.addEventListener('change', () => {
const key = historiaTemplateSelect.value;
if (key && HISTORIA_TEMPLATES[key]) {
const tpl = HISTORIA_TEMPLATES[key];
historiaTemaInput.value = tpl.tema;
if (historiaObjetoMagicoInput) historiaObjetoMagicoInput.value = tpl.objeto;
if (historiaEstilo) historiaEstilo.value = tpl.estilo;
if (historiaFaixaEtaria) historiaFaixaEtaria.value = tpl.faixa;
if (historiaEstiloVisual) historiaEstiloVisual.value = tpl.visual;
}
});
}
if (btnCloseHistoriaModal) historiaModal.addEventListener('click', (e) => { if(e.target === historiaModal) historiaModal.style.display = 'none'; });
if (btnCloseHistoriaModal) btnCloseHistoriaModal.addEventListener('click', () => historiaModal.style.display = 'none');
// Abertura do Modal
if (barBtnHistoria) {
barBtnHistoria.addEventListener('click', () => {
historiaModal.style.display = 'flex';
loadTurmasForHistoria();
if (!currentHistoriaData) {
if (historiaPlaceholderText) historiaPlaceholderText.style.display = 'block';
if (historiaActiveBox) historiaActiveBox.style.display = 'none';
if (historiaAudioPlayerBox) historiaAudioPlayerBox.style.display = 'none';
if (historiaContentTabs) historiaContentTabs.style.display = 'none';
}
});
}
if (btnCloseHistoriaModal) {
historiaModal.addEventListener('click', (e) => { if (e.target === historiaModal) historiaModal.style.display = 'none'; });
btnCloseHistoriaModal.addEventListener('click', () => historiaModal.style.display = 'none');
}
// Histórico
if (btnOpenHistoriaHistory) {
btnOpenHistoriaHistory.addEventListener('click', () => {
historiaHistoryModal.style.display = 'flex';
@@ -7739,93 +7881,358 @@ window.toggleCustomAudio = (btn) => {
});
}
if (btnCloseHistoriaHistory) btnCloseHistoriaHistory.addEventListener('click', () => historiaHistoryModal.style.display = 'none');
historiaHistoryModal.addEventListener('click', (e) => { if(e.target === historiaHistoryModal) historiaHistoryModal.style.display = 'none'; });
historiaHistoryModal.addEventListener('click', (e) => { if (e.target === historiaHistoryModal) historiaHistoryModal.style.display = 'none'; });
// Alternância de Abas
function switchHistoriaTab(tab) {
[btnTabHistoriaCenas, btnTabHistoriaTexto, btnTabHistoriaAtividades].forEach(b => {
if (b) {
b.classList.remove('active');
b.style.background = 'transparent';
b.style.color = 'var(--text-secondary)';
}
});
if (historiaCenasView) historiaCenasView.style.display = 'none';
if (historiaTextoView) historiaTextoView.style.display = 'none';
if (historiaAtividadesView) historiaAtividadesView.style.display = 'none';
if (tab === 'cenas') {
if (btnTabHistoriaCenas) {
btnTabHistoriaCenas.classList.add('active');
btnTabHistoriaCenas.style.background = 'rgba(16,185,129,0.15)';
btnTabHistoriaCenas.style.color = '#34d399';
}
if (historiaCenasView) historiaCenasView.style.display = 'flex';
} else if (tab === 'texto') {
if (btnTabHistoriaTexto) {
btnTabHistoriaTexto.classList.add('active');
btnTabHistoriaTexto.style.background = 'rgba(16,185,129,0.15)';
btnTabHistoriaTexto.style.color = '#34d399';
}
if (historiaTextoView) historiaTextoView.style.display = 'block';
} else if (tab === 'atividades') {
if (btnTabHistoriaAtividades) {
btnTabHistoriaAtividades.classList.add('active');
btnTabHistoriaAtividades.style.background = 'rgba(16,185,129,0.15)';
btnTabHistoriaAtividades.style.color = '#34d399';
}
if (historiaAtividadesView) historiaAtividadesView.style.display = 'flex';
}
}
if (btnTabHistoriaCenas) btnTabHistoriaCenas.addEventListener('click', () => switchHistoriaTab('cenas'));
if (btnTabHistoriaTexto) btnTabHistoriaTexto.addEventListener('click', () => switchHistoriaTab('texto'));
if (btnTabHistoriaAtividades) btnTabHistoriaAtividades.addEventListener('click', () => switchHistoriaTab('atividades'));
// Renderizar Cena Atual
function renderCurrentScene() {
if (!currentHistoriaData || !currentHistoriaData.cenas || currentHistoriaData.cenas.length === 0) return;
const total = currentHistoriaData.cenas.length;
if (currentSceneIndex < 0) currentSceneIndex = 0;
if (currentSceneIndex >= total) currentSceneIndex = total - 1;
const cena = currentHistoriaData.cenas[currentSceneIndex];
if (historiaCenaIndicator) historiaCenaIndicator.textContent = `Cena ${currentSceneIndex + 1} de ${total}`;
if (historiaCenaTitle) historiaCenaTitle.textContent = cena.titulo || `Ato ${currentSceneIndex + 1}`;
if (historiaCenaText) historiaCenaText.textContent = cena.texto || '';
// Imagem da cena
if (cena.imageUrl) {
if (historiaCenaImg) historiaCenaImg.src = cena.imageUrl;
if (historiaCenaImageContainer) historiaCenaImageContainer.style.display = 'block';
if (btnGenerateSceneImage) btnGenerateSceneImage.innerHTML = '🔄 Regenerar Ilustração';
} else {
if (historiaCenaImageContainer) historiaCenaImageContainer.style.display = 'none';
if (btnGenerateSceneImage) btnGenerateSceneImage.innerHTML = '🎨 Gerar Ilustração';
}
if (btnPrevCena) btnPrevCena.disabled = currentSceneIndex === 0;
if (btnNextCena) btnNextCena.disabled = currentSceneIndex === total - 1;
}
if (btnPrevCena) {
btnPrevCena.addEventListener('click', () => {
if (currentSceneIndex > 0) {
currentSceneIndex--;
renderCurrentScene();
}
});
}
if (btnNextCena) {
btnNextCena.addEventListener('click', () => {
if (currentHistoriaData && currentHistoriaData.cenas && currentSceneIndex < currentHistoriaData.cenas.length - 1) {
currentSceneIndex++;
renderCurrentScene();
}
});
}
// Gerar Ilustração da Cena Atual
if (btnGenerateSceneImage) {
btnGenerateSceneImage.addEventListener('click', async () => {
if (!currentHistoriaData || !currentHistoriaData.cenas) return;
const cena = currentHistoriaData.cenas[currentSceneIndex];
if (!cena) return;
const originalBtnHtml = btnGenerateSceneImage.innerHTML;
btnGenerateSceneImage.disabled = true;
btnGenerateSceneImage.innerHTML = '⏳ Ilustrando...';
try {
const res = await fetch('/api/historias/scene-image', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt: cena.promptImagem || cena.texto || 'Cute children book illustration',
estiloVisual: currentHistoriaData.estiloVisual || 'Cartoon colorido',
cenaNumero: currentSceneIndex + 1
})
});
const data = await res.json();
if (res.ok && data.imageUrl) {
cena.imageUrl = data.imageUrl;
renderCurrentScene();
showToast('Ilustração da cena criada com sucesso!', 'success');
} else {
showToast(data.error || 'Erro ao gerar ilustração.', 'error');
}
} catch (err) {
showToast('Erro ao conectar com gerador de ilustração.', 'error');
} finally {
btnGenerateSceneImage.disabled = false;
btnGenerateSceneImage.innerHTML = originalBtnHtml;
}
});
}
// Gerar História Completa
if (btnGenerateHistoria) {
btnGenerateHistoria.addEventListener('click', async () => {
const tema = historiaTemaInput.value.trim();
if (!tema) {
showToast('Por favor, informe o tema da história.', 'error');
showToast('Por favor, informe o tema ou enredo da história.', 'error');
return;
}
btnGenerateHistoria.disabled = true;
historiaResultArea.style.display = 'none';
historiaGenerateLoader.style.display = 'flex';
if (historiaGenerateLoader) historiaGenerateLoader.style.display = 'flex';
if (historiaPlaceholderText) historiaPlaceholderText.style.display = 'none';
try {
const response = await fetch('/api/historias/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
faixaEtaria: historiaFaixaEtaria.value,
paragrafos: historiaParagrafos.value,
personagens: historiaPersonagens.value,
estilo: historiaEstilo.value,
tema: tema
tema: tema,
faixaEtaria: historiaFaixaEtaria ? historiaFaixaEtaria.value : 'Crianças pequenas',
paragrafos: historiaParagrafos ? historiaParagrafos.value : '3',
estilo: historiaEstilo ? historiaEstilo.value : 'Lúdico',
estiloVisual: historiaEstiloVisual ? historiaEstiloVisual.value : 'Cartoon colorido',
personagemPrincipal: historiaPersonagemInput ? historiaPersonagemInput.value.trim() : '',
objetoMagico: historiaObjetoMagicoInput ? historiaObjetoMagicoInput.value.trim() : '',
turmaId: historiaTurmaSelect ? historiaTurmaSelect.value : null
})
});
const data = await response.json();
if (response.ok) {
currentHistoria = data.historia;
historiaTextOutput.innerHTML = window.marked ? marked.parse(currentHistoria) : currentHistoria;
historiaResultArea.style.display = 'flex';
if (response.ok && data.success) {
currentHistoriaData = data;
currentSceneIndex = 0;
// Header
if (historiaActiveTitle) historiaActiveTitle.textContent = data.titulo || 'História Mágica';
if (historiaActiveSubtitle) {
historiaActiveSubtitle.textContent = `Personagem: ${data.personagemPrincipal || 'Turma'}${data.cenas ? data.cenas.length : 3} Cenas • ${data.duracaoLeitura || '3 min'}`;
}
if (historiaBnccBadge) {
historiaBnccBadge.textContent = `BNCC: ${data.bnccCampos || 'EI02EF04 / EI03EF05'}`;
historiaBnccBadge.style.display = 'inline-block';
}
// Player de Áudio
if (data.audioUrl) {
if (historiaAudioPlayer) historiaAudioPlayer.src = data.audioUrl;
if (btnDownloadHistoriaAudio) btnDownloadHistoriaAudio.href = data.audioUrl;
if (historiaAudioPlayerBox) historiaAudioPlayerBox.style.display = 'flex';
if (historiaAudioPlayer) {
historiaAudioPlayer.onplay = () => historiaWaveBars.forEach(b => b.classList.add('wave-active'));
historiaAudioPlayer.onpause = () => historiaWaveBars.forEach(b => b.classList.remove('wave-active'));
historiaAudioPlayer.onended = () => historiaWaveBars.forEach(b => b.classList.remove('wave-active'));
}
} else {
if (historiaAudioPlayerBox) historiaAudioPlayerBox.style.display = 'none';
}
// Abas & Visualização
if (historiaContentTabs) historiaContentTabs.style.display = 'flex';
if (historiaActiveBox) historiaActiveBox.style.display = 'flex';
// Texto completo
if (historiaTextoView) historiaTextoView.textContent = data.historiaCompleta || '';
// Atividades e BNCC
if (historiaBnccDesc) historiaBnccDesc.textContent = data.bnccCampos || 'EI02EF04 / EI03EF05 • Escuta, Fala, Pensamento e Imaginação';
if (historiaPerguntasList) {
historiaPerguntasList.innerHTML = '';
(data.perguntasCompreensao || []).forEach(p => {
const pEl = document.createElement('div');
pEl.style.cssText = 'padding: 4px 0; border-bottom: 1px dashed rgba(255,255,255,0.05);';
pEl.textContent = p;
historiaPerguntasList.appendChild(pEl);
});
}
renderCurrentScene();
switchHistoriaTab('cenas');
showToast('História inventada com sucesso!', 'success');
} else {
showToast(data.error || 'Erro ao inventar história.', 'error');
showToast(data.error || 'Erro ao gerar história.', 'error');
}
} catch (err) {
showToast('Erro de conexão.', 'error');
showToast('Erro de conexão ao gerar história.', 'error');
} finally {
historiaGenerateLoader.style.display = 'none';
if (historiaGenerateLoader) historiaGenerateLoader.style.display = 'none';
btnGenerateHistoria.disabled = false;
}
});
}
if (btnCopyHistoria) {
btnCopyHistoria.addEventListener('click', () => {
if (currentHistoria) {
navigator.clipboard.writeText(currentHistoria);
// Copiar História
if (btnCopyActiveHistoria) {
btnCopyActiveHistoria.addEventListener('click', () => {
if (currentHistoriaData && currentHistoriaData.historiaCompleta) {
navigator.clipboard.writeText(currentHistoriaData.historiaCompleta);
showToast('História copiada para a área de transferência!', 'success');
}
});
}
if (btnDownloadHistoriaPdf) {
btnDownloadHistoriaPdf.addEventListener('click', () => {
if (!currentHistoria) return;
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
// Baixar TXT
if (btnDownloadActiveHistoriaTxt) {
btnDownloadActiveHistoriaTxt.addEventListener('click', () => {
if (!currentHistoriaData) return;
const content = `=========================================
📖 ${currentHistoriaData.titulo || 'HISTÓRIA PEDAGÓGICA'}
=========================================
Personagem Principal: ${currentHistoriaData.personagemPrincipal || 'Turma'}
Objeto Mágico: ${currentHistoriaData.objetoMagico || 'Especial'}
BNCC: ${currentHistoriaData.bnccCampos || 'EI02EF04'}
Duração Estimada: ${currentHistoriaData.duracaoLeitura || '3 min'}
--- HISTÓRIA COMPLETA ---
${currentHistoriaData.historiaCompleta || ''}
--- PERGUNTAS PARA RODA DE CONVERSA ---
${(currentHistoriaData.perguntasCompreensao || []).join('\n')}
Gerado por PedagogIA Assistente da Professora Camila`;
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${(currentHistoriaData.titulo || 'historia').toLowerCase().replace(/\s+/g, '_')}.txt`;
a.click();
URL.revokeObjectURL(url);
});
}
// Musicar História
if (btnSendHistoriaToMusic) {
btnSendHistoriaToMusic.addEventListener('click', () => {
if (!currentHistoriaData) return;
const barBtnMusica = document.getElementById('barBtnMusica');
const musicaTemaInput = document.getElementById('musicaTemaInput');
historiaModal.style.display = 'none';
if (barBtnMusica) barBtnMusica.click();
if (musicaTemaInput) {
musicaTemaInput.value = `Canção sobre a história "${currentHistoriaData.titulo}": ${currentHistoriaData.objetoMagico || ''}, amizade e imaginação`;
}
showToast('História transferida para o Musicando Ideias!', 'info');
});
}
// Exportar PDF Ilustrado
if (btnExportActiveHistoriaPdf) {
btnExportActiveHistoriaPdf.addEventListener('click', () => {
if (!currentHistoriaData) return;
const printWin = window.open('', '_blank');
if (!printWin) {
showToast('Permita pop-ups para gerar o PDF.', 'warning');
return;
}
let cenasHtml = '';
(currentHistoriaData.cenas || []).forEach((c, idx) => {
cenasHtml += `
<div style="margin-bottom: 20px; padding: 15px; border-left: 4px solid #10b981; background: #f8fafc; border-radius: 0 8px 8px 0;">
<h3 style="margin-top: 0; color: #059669; font-size: 1.1rem;">${c.titulo || `Ato ${idx + 1}`}</h3>
${c.imageUrl ? `<div style="text-align: center; margin: 10px 0;"><img src="${c.imageUrl}" style="max-height: 220px; border-radius: 8px;" /></div>` : ''}
<p style="font-size: 1rem; line-height: 1.7; color: #334155; margin: 0;">${c.texto || ''}</p>
</div>
`;
});
let perguntasHtml = '';
(currentHistoriaData.perguntasCompreensao || []).forEach(p => {
perguntasHtml += `<li style="margin-bottom: 6px;">${p}</li>`;
});
printWin.document.write(`
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>História - Assistente PedaGog</title>
<meta charset="UTF-8">
<title>${currentHistoriaData.titulo || 'História Pedagógica'} - Pedagog</title>
<style>
body { font-family: 'Outfit', 'Inter', sans-serif; padding: 40px; color: #333; line-height: 1.6; }
h1, h2, h3 { color: #10b981; margin-top: 24px; margin-bottom: 12px; }
@media print { body { padding: 0; } button { display: none; } }
body { font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 35px; color: #1e293b; line-height: 1.6; max-width: 800px; margin: 0 auto; }
h1 { color: #059669; margin: 0 0 6px 0; font-size: 1.8rem; }
.header-box { border-bottom: 2px solid #e2e8f0; padding-bottom: 14px; margin-bottom: 24px; }
.badge { display: inline-block; background: #d1fae5; color: #065f46; font-size: 0.8rem; padding: 4px 10px; border-radius: 6px; font-weight: bold; margin-right: 6px; }
.btn-print { background: #10b981; color: white; border: none; padding: 10px 20px; border-radius: 8px; font-weight: bold; cursor: pointer; float: right; font-size: 0.95rem; }
@media print { .btn-print { display: none; } body { padding: 0; } }
</style>
</head>
<body>
<div style="max-width: 800px; margin: 0 auto;">
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; margin-bottom: 20px;">
<h1 style="margin: 0;"> História Pedagógica</h1>
<button onclick="window.print()" style="background: #10b981; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: bold;">Imprimir / PDF</button>
<button class="btn-print" onclick="window.print()">🖨 Imprimir / Salvar PDF</button>
<div class="header-box">
<h1>📖 ${currentHistoriaData.titulo || 'História Pedagógica'}</h1>
<div style="margin-top: 8px;">
<span class="badge">Personagem: ${currentHistoriaData.personagemPrincipal || 'Turma'}</span>
<span class="badge">Objeto: ${currentHistoriaData.objetoMagico || 'Especial'}</span>
<span class="badge">BNCC: ${currentHistoriaData.bnccCampos || 'EI02EF04'}</span>
</div>
<div>${window.marked ? marked.parse(currentHistoria) : currentHistoria}</div>
</div>
<h2 style="color: #0f172a; font-size: 1.2rem; border-bottom: 1px solid #cbd5e1; padding-bottom: 6px;">🎬 Cenas da História</h2>
${cenasHtml}
<div style="margin-top: 30px; padding: 18px; background: #f1f5f9; border-radius: 8px;">
<h3 style="margin-top: 0; color: #0f172a; font-size: 1.05rem;">🧩 Roda de Conversa & Atividades</h3>
<ul style="margin: 0; padding-left: 20px; color: #475569; font-size: 0.95rem;">
${perguntasHtml}
</ul>
</div>
<div style="margin-top: 40px; text-align: center; font-size: 0.8rem; color: #94a3b8; border-top: 1px solid #e2e8f0; padding-top: 12px;">
Pedagog Estúdio de Literatura Infantil da Professora Camila Martella Gasparini Reifonas
</div>
</body>
</html>
`);
printWindow.document.close();
printWin.document.close();
});
}
// Carregar Histórico
async function loadHistoriaHistory() {
const listContainer = document.getElementById('historiaHistoryList');
if (!listContainer) return;
listContainer.innerHTML = '<p style="text-align:center; color:var(--text-secondary);">Carregando...</p>';
listContainer.innerHTML = '<p style="text-align:center; color:var(--text-secondary);">Carregando histórias...</p>';
try {
const response = await fetch('/api/historias/list');
const data = await response.json();
@@ -7835,19 +8242,24 @@ window.toggleCustomAudio = (btn) => {
const date = new Date(item.created_at).toLocaleString('pt-BR');
const div = document.createElement('div');
div.className = 'history-item-card';
div.style.cssText = 'background: rgba(255,255,255,0.02); padding: 14px; border-radius: 8px; border: 1px solid var(--border-light); display: flex; flex-direction: column; gap: 8px; cursor: pointer; position: relative;';
div.style.cssText = 'background: rgba(255,255,255,0.02); padding: 14px; border-radius: 8px; border: 1px solid var(--border-light); display: flex; flex-direction: column; gap: 6px; cursor: pointer; position: relative;';
div.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: flex-start; padding-right: 30px;">
<strong style="color: var(--brand-green); font-size: 0.95rem;">${item.faixa_etaria} (${item.estilo})</strong>
<span style="font-size: 0.75rem; color: var(--text-secondary);">${date}</span>
<strong style="color: #34d399; font-size: 0.95rem;">${item.titulo || item.tema || 'História'}</strong>
<span style="font-size: 0.72rem; color: var(--text-secondary);">${date}</span>
</div>
<p style="margin: 0; font-size: 0.82rem; color: var(--text-primary); opacity: 0.9;">Tema: ${item.tema}</p>
<div style="display: flex; gap: 8px; font-size: 0.72rem; color: var(--text-secondary);">
<span>👶 ${item.faixa_etaria}</span>
${item.personagem_principal ? `<span>• 👤 ${item.personagem_principal}</span>` : ''}
${item.audio_url ? `<span>• 🎙️ Áudio MP3</span>` : ''}
</div>
<p style="margin: 0; font-size: 0.85rem; color: var(--text-primary); opacity: 0.9;">Tema: ${item.tema}</p>
<button class="btn-delete-historia" data-id="${item.id}" style="position: absolute; top: 12px; right: 12px; background: transparent; border: none; color: #ef4444; cursor: pointer; font-size: 1.1rem; padding: 4px; border-radius: 4px;" title="Excluir">🗑</button>
`;
div.addEventListener('click', async (e) => {
if (e.target.closest('.btn-delete-historia')) {
e.stopPropagation();
if (confirm('Tem certeza que deseja excluir?')) {
if (confirm('Tem certeza que deseja excluir esta história?')) {
try {
const delRes = await fetch(`/api/historias/${item.id}`, { method: 'DELETE' });
if (delRes.ok) { showToast('História excluída.', 'success'); loadHistoriaHistory(); }
@@ -7859,19 +8271,69 @@ window.toggleCustomAudio = (btn) => {
const res = await fetch(`/api/historias/${item.id}`);
const detail = await res.json();
if (res.ok) {
currentHistoria = detail.historia;
historiaTextOutput.innerHTML = window.marked ? marked.parse(currentHistoria) : currentHistoria;
historiaResultArea.style.display = 'flex';
currentHistoriaData = {
id: detail.id,
titulo: detail.titulo || `História: ${detail.tema}`,
personagemPrincipal: detail.personagem_principal || '',
objetoMagico: detail.objeto_magico || '',
historiaCompleta: detail.historia || '',
cenas: typeof detail.cenas === 'string' ? JSON.parse(detail.cenas) : (detail.cenas || []),
perguntasCompreensao: typeof detail.perguntas_compreensao === 'string' ? JSON.parse(detail.perguntas_compreensao) : (detail.perguntas_compreensao || []),
bnccCampos: detail.bncc || 'EI02EF04 / EI03EF05',
duracaoLeitura: detail.duracao_leitura || '3 min',
complexidade: detail.complexidade || 'Lúdica',
audioUrl: detail.audio_url || null,
estiloVisual: detail.estilo_visual || 'Cartoon colorido'
};
currentSceneIndex = 0;
if (historiaActiveTitle) historiaActiveTitle.textContent = currentHistoriaData.titulo;
if (historiaActiveSubtitle) {
historiaActiveSubtitle.textContent = `Personagem: ${currentHistoriaData.personagemPrincipal || 'Turma'}${currentHistoriaData.cenas ? currentHistoriaData.cenas.length : 3} Cenas`;
}
if (historiaBnccBadge) {
historiaBnccBadge.textContent = `BNCC: ${currentHistoriaData.bnccCampos}`;
historiaBnccBadge.style.display = 'inline-block';
}
if (currentHistoriaData.audioUrl) {
if (historiaAudioPlayer) historiaAudioPlayer.src = currentHistoriaData.audioUrl;
if (btnDownloadHistoriaAudio) btnDownloadHistoriaAudio.href = currentHistoriaData.audioUrl;
if (historiaAudioPlayerBox) historiaAudioPlayerBox.style.display = 'flex';
} else {
if (historiaAudioPlayerBox) historiaAudioPlayerBox.style.display = 'none';
}
if (historiaContentTabs) historiaContentTabs.style.display = 'flex';
if (historiaActiveBox) historiaActiveBox.style.display = 'flex';
if (historiaPlaceholderText) historiaPlaceholderText.style.display = 'none';
if (historiaTextoView) historiaTextoView.textContent = currentHistoriaData.historiaCompleta;
if (historiaBnccDesc) historiaBnccDesc.textContent = currentHistoriaData.bnccCampos;
if (historiaPerguntasList) {
historiaPerguntasList.innerHTML = '';
(currentHistoriaData.perguntasCompreensao || []).forEach(p => {
const pEl = document.createElement('div');
pEl.style.cssText = 'padding: 4px 0; border-bottom: 1px dashed rgba(255,255,255,0.05);';
pEl.textContent = p;
historiaPerguntasList.appendChild(pEl);
});
}
renderCurrentScene();
switchHistoriaTab('cenas');
historiaHistoryModal.style.display = 'none';
}
} catch (err) { showToast('Erro ao carregar.', 'error'); }
} catch (err) { showToast('Erro ao carregar história.', 'error'); }
});
listContainer.appendChild(div);
});
} else {
listContainer.innerHTML = '<p style="text-align:center; color:var(--text-secondary);">Nenhum histórico.</p>';
listContainer.innerHTML = '<p style="text-align:center; color:var(--text-secondary);">Nenhuma história salva ainda.</p>';
}
} catch (err) { listContainer.innerHTML = '<p style="text-align:center; color:#ef4444;">Erro ao carregar.</p>'; }
} catch (err) {
listContainer.innerHTML = '<p style="text-align:center; color:#ef4444;">Erro ao carregar histórico de histórias.</p>';
}
}
if (document.readyState === 'loading') {
+248 -62
View File
@@ -1208,88 +1208,274 @@
</div>
<!-- ========================================== -->
<!-- MODAL: INVENTANDO HISTORIAS -->
<!-- MODAL: INVENTANDO HISTÓRIAS (ESTÚDIO PROFISSIONAL) -->
<!-- ========================================== -->
<div id="historiaModal" class="settings-modal" style="display: none;">
<div class="settings-modal-content" style="max-width: 600px; width: 95%; max-height: 90vh; display: flex; flex-direction: column;">
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light);">
<h3 style="display: flex; align-items: center; gap: 8px; font-family: 'Outfit', sans-serif;">✍️ Inventando Histórias PedaGog</h3>
<button id="btnCloseHistoriaModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem;">&times;</button>
</div>
<div class="settings-modal-content" style="max-width: 1080px; width: 95%; max-height: 92vh; display: flex; flex-direction: column;">
<div style="padding: 12px 20px; display: flex; justify-content: flex-end; border-bottom: 1px solid var(--border-light);">
<button id="btnOpenHistoriaHistory" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-primary); padding: 6px 12px; border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 6px; font-size: 0.85rem;">🕒 Ver Histórico</button>
<!-- Header -->
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light); padding: 14px 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 1.4rem;">📖</span>
<div>
<h3 style="margin: 0; font-family: 'Outfit', sans-serif; font-size: 1.15rem; color: var(--text-primary);">Inventando Histórias • Estúdio PedagogIA</h3>
<span style="font-size: 0.75rem; color: var(--text-secondary);">Narrativa em 3 atos, personagens da turma, ilustrações por cena e áudio narrado</span>
</div>
</div>
<div style="display: flex; gap: 8px; align-items: center;">
<button type="button" id="btnOpenHistoriaHistory" style="background: rgba(16, 185, 129, 0.12); border: 1px solid rgba(16, 185, 129, 0.3); color: #34d399; padding: 6px 12px; border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 6px; font-size: 0.82rem; font-weight: 600;">🕒 Histórico</button>
<button type="button" id="btnCloseHistoriaModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem; line-height: 1;">&times;</button>
</div>
</div>
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; flex: 1;">
<!-- Body em 2 Colunas -->
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: row; gap: 24px; flex: 1; flex-wrap: wrap;">
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">1. Faixa Etária</label>
<select id="historiaFaixaEtaria" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Bebês (0 a 1 ano e 6 meses)">Bebês (0 a 1 ano e 6 meses)</option>
<option value="Crianças bem pequenas (1 ano e 7 meses a 3 anos e 11 meses)">Crianças bem pequenas (1 ano e 7 meses a 3 anos e 11 meses)</option>
<option value="Crianças pequenas (4 anos a 5 anos e 11 meses)" selected>Crianças pequenas (4 anos a 5 anos e 11 meses)</option>
<option value="Ensino Fundamental I (6 a 10 anos)">Ensino Fundamental I (6 a 10 anos)</option>
</select>
</div>
<!-- Lado Esquerdo: Criação & Parâmetros Narrativos -->
<div id="historiaLeftPanel" style="flex: 1; min-width: 320px; display: flex; flex-direction: column; gap: 14px;">
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 6px; font-size: 0.92rem;">✨ 1. Inspiração & Personagens</h4>
<div style="display: flex; gap: 12px; align-items: flex-start;">
<div class="settings-group" style="flex: 1;">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Tamanho (Parágrafos)</label>
<select id="historiaParagrafos" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="2">2 Parágrafos</option>
<option value="3" selected>3 Parágrafos</option>
<option value="5">5 Parágrafos</option>
<option value="7">7 Parágrafos</option>
<!-- TEMPLATES RÁPIDOS DE HISTÓRIA -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Modelos de História (Templates)</label>
<select id="historiaTemplateSelect" class="obs-select" style="width: 100%; margin-top: 4px; color: #10b981; font-weight: 600;">
<option value="">✨ Escolha um modelo de história...</option>
<option value="arca">📦 A Arca da Imaginação e as Descobertas</option>
<option value="horta">🌱 O Mistério da Horta Encantada</option>
<option value="dinossauro">🦖 O Pequeno Dinossauro que Queria Amigos</option>
<option value="chuva">🌧️ O Dia em que a Chuva Dançou no Parque</option>
<option value="bolhas">🧼 O Reino Mágico das Bolhas de Sabão</option>
<option value="brinquedos">🧸 A Grande Aventura dos Brinquedos Perdidos</option>
<option value="emocoes">💖 O Monstrinho das Cores e dos Sentimentos</option>
</select>
</div>
<div class="settings-group" style="flex: 1;">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Personagens</label>
<input type="number" id="historiaPersonagens" value="2" min="1" max="10" class="obs-input" style="width: 100%; margin-top: 4px; padding: 10px;">
<!-- TURMA & PERSONAGEM PRINCIPAL -->
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div class="settings-group" style="flex: 1; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Vincular à Turma</label>
<select id="historiaTurmaSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="">-- Selecionar Turma --</option>
</select>
</div>
<div class="settings-group" style="flex: 1; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Personagem da Turma</label>
<input type="text" id="historiaPersonagemInput" placeholder="Ex: João, Maria, toda a turma..." class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
</div>
</div>
</div>
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">3. Estilo da Narrativa</label>
<select id="historiaEstilo" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Narração Direta">Narração Direta (Apenas Narrador)</option>
<option value="Dialogada">Dialogada (Com fala dos personagens)</option>
</select>
</div>
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">4. Tema da História</label>
<input type="text" id="historiaTemaInput" placeholder="Ex: Uma fada que não sabia voar, a importância de dividir..." class="obs-input" style="width: 100%; margin-top: 4px; padding: 10px;">
</div>
<button type="button" id="btnGenerateHistoria" style="width: 100%; padding: 12px; background: #10a37f; 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(16, 163, 127, 0.3); transition: all 0.2s;">✍️ Inventar História</button>
<div id="historiaGenerateLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; margin-top: 15px;">
<div class="spinner"></div>
<span style="color: var(--text-secondary); font-size: 0.9rem;">Inventando a história...</span>
</div>
<div id="historiaResultArea" style="display: none; flex-direction: column; gap: 12px; margin-top: 20px;">
<div style="background: rgba(16, 185, 129, 0.05); border: 1px solid rgba(16, 185, 129, 0.2); padding: 15px; border-radius: 8px;">
<h4 style="color: var(--brand-green); margin-top: 0; margin-bottom: 10px;">História Gerada:</h4>
<div id="historiaTextOutput" style="font-size: 0.95rem; line-height: 1.6; white-space: pre-wrap; color: var(--text-primary);"></div>
<!-- OBJETO MÁGICO / ELEMENTO DISPARADOR -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Objeto Mágico ou Elemento Disparador</label>
<input type="text" id="historiaObjetoMagicoInput" value="Arca da Imaginação" placeholder="Ex: Arca mágica, semente brilhante, lupa encantada..." class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end;">
<button id="btnCopyHistoria" style="background: var(--bg-secondary); border: 1px solid var(--border-light); color: var(--text-primary); padding: 8px 16px; border-radius: 6px; cursor: pointer;">📋 Copiar</button>
<button id="btnDownloadHistoriaPdf" style="background: var(--brand-green); border: none; color: white; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: bold;">📄 Salvar PDF</button>
<!-- TEMA / ENREDO -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Tema / Enredo da História</label>
<textarea id="historiaTemaInput" placeholder="Ex: Crianças encontram uma arca mágica no quintal da escola e precisam cooperar para descobrir os segredos..." class="obs-input" style="width: 100%; min-height: 55px; padding: 8px; margin-top: 4px; font-size: 0.85rem; resize: vertical;"></textarea>
</div>
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 6px; margin-top: 4px; font-size: 0.92rem;">🏗️ 2. Estrutura Narrativa & Estilo Visual</h4>
<!-- IDADE & ESTRUTURA DOS ATOS -->
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div class="settings-group" style="flex: 1; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Faixa Etária</label>
<select id="historiaFaixaEtaria" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Bebês (0 a 1 ano e 6 meses)">Bebês (0 a 1a6m)</option>
<option value="Crianças bem pequenas (1 ano e 7 meses a 3 anos e 11 meses)">Bem pequenas (1a7m a 3a11m)</option>
<option value="Crianças pequenas (4 anos a 5 anos e 11 meses)" selected>Crianças pequenas (4 a 5a11m)</option>
<option value="Ensino Fundamental I (6 a 10 anos)">Ensino Fundamental I</option>
</select>
</div>
<div class="settings-group" style="flex: 1; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Cenas / Atos</label>
<select id="historiaParagrafos" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="3" selected>🎬 3 Cenas (Ato 1, Ato 2, Ato 3)</option>
<option value="4">🎬 4 Cenas Narrativas</option>
<option value="5">🎬 5 Cenas Detalhadas</option>
</select>
</div>
</div>
<!-- ESTILO NARRATIVO & ESTILO VISUAL -->
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div class="settings-group" style="flex: 1; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Estilo Narrativo</label>
<select id="historiaEstilo" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Lúdico, Afetivo e Dialogado" selected>💬 Lúdico & Dialogado</option>
<option value="Contação Clássica com Suspense Leve">📖 Contação Clássica</option>
<option value="Roda de Conversa Interativa">🗣️ Interativo para Roda</option>
<option value="Aventura e Descoberta na Escola">🚀 Aventura & Cooperação</option>
</select>
</div>
<div class="settings-group" style="flex: 1; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Estilo das Ilustrações</label>
<select id="historiaEstiloVisual" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Cartoon colorido e acolhedor" selected>🎨 Cartoon Colorido</option>
<option value="Aquarela suave infantil">🖌️ Aquarela Suave</option>
<option value="Livro infantil clássico e detalhado">📚 Livro Infantil Clássico</option>
<option value="Giz de cera e textura lúdica">🖍️ Giz de Cera Infantil</option>
</select>
</div>
</div>
<!-- BOTÃO GERAR HISTÓRIA -->
<button type="button" id="btnGenerateHistoria" style="width: 100%; padding: 12px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; border: none; border-radius: 10px; font-weight: 700; font-size: 0.95rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.3); transition: all 0.2s; margin-top: 6px;">
<span>📖 Inventar História Mágica (3 Atos + Cenas)</span>
</button>
<div id="historiaGenerateLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.85rem; padding: 10px; background: rgba(255, 255, 255, 0.02); border-radius: 8px;">
<div class="media-spinner" style="width: 18px; height: 18px; border-width: 2px; border-top-color: #10b981;"></div>
<span id="historiaLoaderText">Construindo narrativa em 3 atos e áudio...</span>
</div>
</div>
<!-- Lado Direito: Visualizador de Cenas, Ilustrações e Leitor -->
<div id="historiaRightPanel" style="flex: 1.3; min-width: 360px; display: flex; flex-direction: column; gap: 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 6px;">
<h4 style="margin: 0; color: var(--text-primary); font-size: 0.95rem;">📚 Livro de Histórias & Cenas</h4>
<div id="historiaBnccBadge" style="display: none; font-size: 0.72rem; background: rgba(16, 185, 129, 0.15); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.3); padding: 3px 8px; border-radius: 6px; font-weight: 600;">
BNCC: EI02EF04
</div>
</div>
<!-- ÁREA DO PLAYER DE ÁUDIO NARRADO -->
<div id="historiaAudioPlayerBox" style="display: none; background: linear-gradient(135deg, rgba(16,185,129,0.12), rgba(59,130,246,0.08)); border: 1px solid rgba(16,185,129,0.3); border-radius: 12px; padding: 14px; flex-direction: column; gap: 10px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div style="display: flex; align-items: center; gap: 8px;">
<span style="font-size: 1.2rem;">🎙️</span>
<div>
<div style="font-size: 0.85rem; font-weight: 700; color: var(--text-primary);" id="historiaPlayerTitle">Narração por PedagogIA</div>
<div style="font-size: 0.72rem; color: var(--text-secondary);">Contação de Histórias • Formato MP3</div>
</div>
</div>
<div style="display: flex; gap: 6px; align-items: center;">
<a id="btnDownloadHistoriaAudio" href="#" download="historia_narrada.mp3" style="background: #10b981; color: white; border: none; padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; font-weight: 700; text-decoration: none; display: flex; align-items: center; gap: 4px;">
📥 Baixar Narração
</a>
</div>
</div>
<!-- Waveform / Equalizer Animado -->
<div id="historiaWaveformVisualizer" style="display: flex; align-items: center; justify-content: center; gap: 3px; height: 28px; padding: 4px; background: rgba(0,0,0,0.15); border-radius: 6px;">
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 30%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 60%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 90%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 45%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 75%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 35%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 85%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 50%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 95%; background: #10b981; border-radius: 2px;"></div>
<div class="wave-bar wave-bar-historia" style="width: 4px; height: 40%; background: #10b981; border-radius: 2px;"></div>
</div>
<audio id="historiaAudioPlayer" controls style="width: 100%; margin-top: 2px;"></audio>
</div>
<!-- ABAS DE NAVEGAÇÃO -->
<div id="historiaContentTabs" style="display: none; gap: 6px; border-bottom: 1px solid var(--border-light); padding-bottom: 8px;">
<button type="button" id="btnTabHistoriaCenas" class="tab-btn active" style="font-size: 0.78rem; padding: 6px 12px; background: rgba(16,185,129,0.15); border: none; cursor: pointer; color: #34d399; border-radius: 6px; font-weight: bold;">🎬 Cenas & Ilustrações</button>
<button type="button" id="btnTabHistoriaTexto" class="tab-btn" style="font-size: 0.78rem; padding: 6px 12px; background: transparent; border: none; cursor: pointer; color: var(--text-secondary); border-radius: 6px;">📖 História Completa</button>
<button type="button" id="btnTabHistoriaAtividades" class="tab-btn" style="font-size: 0.78rem; padding: 6px 12px; background: transparent; border: none; cursor: pointer; color: var(--text-secondary); border-radius: 6px;">🧩 Perguntas & BNCC</button>
</div>
<!-- ÁREA DO CONTEÚDO ATIVO -->
<div id="historiaDisplayArea" style="display: flex; flex-direction: column; gap: 12px; flex: 1;">
<div id="historiaPlaceholderText" style="text-align: center; padding: 40px 20px; color: var(--text-secondary); background: rgba(255,255,255,0.02); border: 1px dashed var(--border-light); border-radius: 10px;">
<span style="font-size: 2.5rem; display: block; margin-bottom: 10px;">📖</span>
<strong style="color: var(--text-primary); font-size: 0.95rem;">Pronto para inventar uma história inesquecível?</strong>
<p style="font-size: 0.82rem; margin-top: 6px; opacity: 0.8;">Escolha um modelo ou digite o tema ao lado e clique em "Inventar História" para gerar os atos narrativos, ilustrações e áudio narrado.</p>
</div>
<!-- CONTAINER DA HISTÓRIA ATIVA -->
<div id="historiaActiveBox" style="display: none; background: rgba(255,255,255,0.03); border: 1px solid var(--border-light); border-radius: 12px; padding: 16px; flex-direction: column; gap: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px dashed var(--border-light); padding-bottom: 8px; flex-wrap: wrap; gap: 8px;">
<div>
<h4 id="historiaActiveTitle" style="margin: 0; color: #10b981; font-size: 1.05rem; font-family: 'Fredoka', sans-serif;">Título da História</h4>
<span id="historiaActiveSubtitle" style="font-size: 0.72rem; color: var(--text-secondary);">Personagem: João • 3 Cenas</span>
</div>
<div style="display: flex; gap: 6px;">
<button type="button" id="btnCopyActiveHistoria" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 4px 8px; border-radius: 6px; font-size: 0.72rem; cursor: pointer;" title="Copiar história">📋 Copiar</button>
<button type="button" id="btnDownloadActiveHistoriaTxt" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 4px 8px; border-radius: 6px; font-size: 0.72rem; cursor: pointer;" title="Baixar TXT">💾 TXT</button>
<button type="button" id="btnSendHistoriaToMusic" style="background: rgba(168,85,247,0.15); border: 1px solid rgba(168,85,247,0.3); color: #c084fc; padding: 4px 8px; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: 600;" title="Criar música desta história">🎵 Musicar</button>
<button type="button" id="btnExportActiveHistoriaPdf" style="background: #10b981; color: white; border: none; padding: 4px 10px; border-radius: 6px; font-size: 0.72rem; font-weight: 700; cursor: pointer;" title="Salvar PDF formatado">📄 PDF</button>
</div>
</div>
<!-- VIEW 1: LEITOR INTERATIVO DE CENAS COM ILUSTRAÇÕES -->
<div id="historiaCenasView" style="display: flex; flex-direction: column; gap: 12px;">
<!-- Barra de Navegação entre Cenas -->
<div style="display: flex; justify-content: space-between; align-items: center; background: rgba(0,0,0,0.12); padding: 6px 12px; border-radius: 8px;">
<button type="button" id="btnPrevCena" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-primary); padding: 4px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer;">◀ Cena Anterior</button>
<span id="historiaCenaIndicator" style="font-size: 0.8rem; font-weight: 700; color: #34d399;">Cena 1 de 3</span>
<button type="button" id="btnNextCena" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-primary); padding: 4px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer;">Próxima Cena ▶</button>
</div>
<!-- Card da Cena Atual -->
<div id="historiaCenaCard" style="display: flex; flex-direction: column; gap: 10px; background: rgba(0,0,0,0.06); padding: 14px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h5 id="historiaCenaTitle" style="margin: 0; color: #34d399; font-size: 0.95rem;">Ato 1: O Descobrimento</h5>
<button type="button" id="btnGenerateSceneImage" style="background: rgba(59,130,246,0.15); border: 1px solid rgba(59,130,246,0.3); color: #60a5fa; padding: 4px 8px; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: 600; display: flex; align-items: center; gap: 4px;">
🎨 Gerar Ilustração
</button>
</div>
<!-- Container da Imagem da Cena -->
<div id="historiaCenaImageContainer" style="display: none; width: 100%; border-radius: 8px; overflow: hidden; border: 1px solid var(--border-light); max-height: 200px; text-align: center; background: rgba(0,0,0,0.2);">
<img id="historiaCenaImg" src="" alt="Ilustração da Cena" style="max-height: 200px; width: auto; object-fit: contain;">
</div>
<!-- Texto da Cena -->
<div id="historiaCenaText" style="font-family: 'Fredoka', 'Outfit', sans-serif; font-size: 1.05rem; line-height: 1.7; color: var(--text-primary); white-space: pre-wrap; max-height: 220px; overflow-y: auto;"></div>
</div>
</div>
<!-- VIEW 2: HISTÓRIA COMPLETA -->
<div id="historiaTextoView" style="display: none; font-family: 'Fredoka', 'Outfit', sans-serif; font-size: 1.05rem; line-height: 1.7; color: var(--text-primary); white-space: pre-wrap; background: rgba(0,0,0,0.08); padding: 14px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.05); max-height: 300px; overflow-y: auto;"></div>
<!-- VIEW 3: ATIVIDADES & BNCC -->
<div id="historiaAtividadesView" style="display: none; flex-direction: column; gap: 12px;">
<div style="background: rgba(16,185,129,0.08); border: 1px solid rgba(16,185,129,0.2); border-radius: 8px; padding: 12px; font-size: 0.85rem; color: #a7f3d0; line-height: 1.5;">
<strong>🎯 Objetivos de Aprendizagem & BNCC:</strong><br>
<span id="historiaBnccDesc">EI02EF04 / EI03EF05 • Escuta, Fala, Pensamento e Imaginação</span>
</div>
<div style="background: rgba(255,255,255,0.03); border: 1px solid var(--border-light); border-radius: 8px; padding: 14px;">
<strong style="color: #34d399; font-size: 0.88rem; display: block; margin-bottom: 8px;">🧩 Perguntas de Compreensão & Roda de Conversa:</strong>
<div id="historiaPerguntasList" style="display: flex; flex-direction: column; gap: 6px; font-size: 0.85rem; color: var(--text-primary); line-height: 1.4;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal: Histórico de Histórias -->
<div id="historiaHistoryModal" class="settings-modal" style="display: none; z-index: 10001;">
<div class="settings-modal-content" style="max-width: 500px; width: 95%; max-height: 80vh; display: flex; flex-direction: column;">
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light);">
<h3 style="display: flex; align-items: center; gap: 8px; font-family: 'Outfit', sans-serif;">🕒 Histórico de Histórias</h3>
<button id="btnCloseHistoriaHistory" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem;">&times;</button>
<div class="settings-modal-content" style="max-width: 540px; width: 95%; max-height: 80vh; display: flex; flex-direction: column;">
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light); padding: 14px 20px; display: flex; justify-content: space-between; align-items: center;">
<h3 style="margin: 0; display: flex; align-items: center; gap: 8px; font-family: 'Outfit', sans-serif; font-size: 1.1rem; color: #34d399;">🕒 Histórico de Histórias Geradas</h3>
<button type="button" id="btnCloseHistoriaHistory" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem; line-height: 1;">&times;</button>
</div>
<div id="historiaHistoryList" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px;"></div>
<div id="historiaHistoryList" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; flex: 1;"></div>
</div>
</div>
<!-- Modal: Planejamento Mind Lab -->
+12 -1
View File
@@ -4134,7 +4134,8 @@ code {
@media (max-width: 768px) {
#musicaLeftPanel, #musicaRightPanel,
#poesiaLeftPanel, #poesiaRightPanel {
#poesiaLeftPanel, #poesiaRightPanel,
#historiaLeftPanel, #historiaRightPanel {
min-width: 100% !important;
width: 100% !important;
}
@@ -4150,6 +4151,16 @@ code {
100% { height: 40%; background-color: #fbbf24; }
}
.wave-bar-historia.wave-active {
animation: wave-pulse-historia 0.8s infinite ease-in-out alternate;
}
@keyframes wave-pulse-historia {
0% { height: 20%; background-color: #10b981; }
50% { height: 95%; background-color: #34d399; }
100% { height: 40%; background-color: #059669; }
}