🚀 Auto-deploy: Implementação das ferramentas Garatujas e BrincarAtivo

This commit is contained in:
2026-06-04 23:44:53 +00:00
parent 6866d565ec
commit dcabe19edd
3 changed files with 984 additions and 0 deletions
+557
View File
@@ -4713,9 +4713,566 @@ const initApp = () => {
</body>
</html>
`);
});
}
// ============================================================
// GARATUJAS (ANALISADOR DE TRAÇADO INFANTIL)
// ============================================================
const garatujasModal = document.getElementById('garatujasModal');
const btnCloseGaratujasModal = document.getElementById('btnCloseGaratujasModal');
const barBtnGaratujas = document.getElementById('barBtnGaratujas');
const tabGaratujasNova = document.getElementById('tabGaratujasNova');
const tabGaratujasHistorico = document.getElementById('tabGaratujasHistorico');
const contentGaratujasNova = document.getElementById('contentGaratujasNova');
const contentGaratujasHistorico = document.getElementById('contentGaratujasHistorico');
const garatujasAlunoInput = document.getElementById('garatujasAlunoInput');
const garatujaFileInput = document.getElementById('garatujaFileInput');
const btnUploadGaratuja = document.getElementById('btnUploadGaratuja');
const garatujaFileStatus = document.getElementById('garatujaFileStatus');
const garatujaPreviewContainer = document.getElementById('garatujaPreviewContainer');
const garatujaImgPreview = document.getElementById('garatujaImgPreview');
const btnGenerateGaratuja = document.getElementById('btnGenerateGaratuja');
const garatujaLoader = document.getElementById('garatujaLoader');
const garatujaLoaderText = document.getElementById('garatujaLoaderText');
const garatujaResultArea = document.getElementById('garatujaResultArea');
const garatujaReportArea = document.getElementById('garatujaReportArea');
const btnCopyGaratujaReport = document.getElementById('btnCopyGaratujaReport');
const btnPdfGaratujaReport = document.getElementById('btnPdfGaratujaReport');
const garatujasListContainer = document.getElementById('garatujasListContainer');
const garatujasNoData = document.getElementById('garatujasNoData');
let activeGaratujaReport = null;
// Abas do Garatujas
if (tabGaratujasNova) {
tabGaratujasNova.addEventListener('click', () => {
tabGaratujasNova.classList.add('active');
tabGaratujasHistorico.classList.remove('active');
contentGaratujasNova.style.display = 'block';
contentGaratujasHistorico.style.display = 'none';
});
}
if (tabGaratujasHistorico) {
tabGaratujasHistorico.addEventListener('click', () => {
tabGaratujasHistorico.classList.add('active');
tabGaratujasNova.classList.remove('active');
contentGaratujasNova.style.display = 'none';
contentGaratujasHistorico.style.display = 'block';
loadGaratujasHistory();
});
}
// Abrir Modal
if (barBtnGaratujas) {
barBtnGaratujas.addEventListener('click', () => {
garatujasModal.style.display = 'flex';
garatujasAlunoInput.value = '';
garatujaFileInput.value = '';
garatujaFileStatus.textContent = 'Nenhum arquivo selecionado';
garatujaPreviewContainer.style.display = 'none';
garatujaResultArea.style.display = 'none';
garatujaLoader.style.display = 'none';
btnGenerateGaratuja.disabled = false;
activeGaratujaReport = null;
if (tabGaratujasNova) tabGaratujasNova.click();
});
}
// Fechar Modal
if (btnCloseGaratujasModal) {
btnCloseGaratujasModal.addEventListener('click', () => {
garatujasModal.style.display = 'none';
});
}
window.addEventListener('click', (e) => {
if (e.target === garatujasModal) {
garatujasModal.style.display = 'none';
}
});
// Upload trigger
if (btnUploadGaratuja) {
btnUploadGaratuja.addEventListener('click', () => {
garatujaFileInput.click();
});
}
if (garatujaFileInput) {
garatujaFileInput.addEventListener('change', () => {
const file = garatujaFileInput.files[0];
if (file) {
garatujaFileStatus.textContent = file.name;
const reader = new FileReader();
reader.onload = (e) => {
garatujaImgPreview.src = e.target.result;
garatujaPreviewContainer.style.display = 'block';
};
reader.readAsDataURL(file);
} else {
garatujaFileStatus.textContent = 'Nenhum arquivo selecionado';
garatujaPreviewContainer.style.display = 'none';
}
});
}
// Gerar Análise
if (btnGenerateGaratuja) {
btnGenerateGaratuja.addEventListener('click', async () => {
const alunoName = garatujasAlunoInput.value.trim();
const file = garatujaFileInput.files[0];
if (!alunoName) {
alert('Por favor, informe o nome do aluno.');
return;
}
if (!file) {
alert('Por favor, selecione a foto do desenho.');
return;
}
btnGenerateGaratuja.disabled = true;
garatujaLoader.style.display = 'flex';
garatujaResultArea.style.display = 'none';
garatujaLoaderText.textContent = 'Enviando imagem pedagógica...';
const formData = new FormData();
formData.append('alunoName', alunoName);
formData.append('image', file);
let statusMsgIdx = 0;
const statusMessages = [
'Carregando desenho infantil...',
'IA escaneando os traços e formas...',
'Analisando simetria e coordenação motora...',
'Definindo estágio evolutivo do desenho...',
'Criando sugestões de atividades formativas...',
'Finalizando laudo psicopedagógico...'
];
const statusInterval = setInterval(() => {
if (statusMsgIdx < statusMessages.length) {
garatujaLoaderText.textContent = statusMessages[statusMsgIdx];
statusMsgIdx++;
} else {
garatujaLoaderText.textContent = 'Formatando parecer...';
}
}, 4500);
try {
const response = await fetch('/api/garatujas/analyze', {
method: 'POST',
body: formData
});
clearInterval(statusInterval);
if (!response.ok) {
const errData = await response.json();
throw new Error(errData.error || 'Erro desconhecido');
}
const data = await response.json();
activeGaratujaReport = data;
if (window.marked) {
garatujaReportArea.innerHTML = marked.parse(data.analise);
} else {
garatujaReportArea.textContent = data.analise;
}
garatujaLoader.style.display = 'none';
garatujaResultArea.style.display = 'flex';
btnGenerateGaratuja.disabled = false;
} catch (err) {
clearInterval(statusInterval);
console.error(err);
alert('Erro na análise: ' + err.message);
garatujaLoader.style.display = 'none';
btnGenerateGaratuja.disabled = false;
}
});
}
// Copiar Parecer Garatujas
if (btnCopyGaratujaReport) {
btnCopyGaratujaReport.addEventListener('click', () => {
if (activeGaratujaReport && activeGaratujaReport.analise) {
navigator.clipboard.writeText(activeGaratujaReport.analise)
.then(() => {
btnCopyGaratujaReport.textContent = '✅ Copiado!';
setTimeout(() => { btnCopyGaratujaReport.textContent = '📋 Copiar'; }, 2000);
});
}
});
}
// Baixar PDF Garatujas
if (btnPdfGaratujaReport) {
btnPdfGaratujaReport.addEventListener('click', () => {
if (!activeGaratujaReport) return;
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>Garatujas - Laudo do Desenho</title>
<style>
body { font-family: 'Outfit', 'Inter', sans-serif; padding: 40px; color: #333; line-height: 1.6; }
h1, h2, h3 { color: #ea580c; margin-top: 24px; margin-bottom: 12px; }
h1 { border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; font-size: 22px; }
.meta-box { background: #f8fafc; padding: 16px; border-radius: 8px; margin-bottom: 24px; border: 1px solid #e2e8f0; display: flex; gap: 16px; align-items: center; }
.meta-label { font-weight: bold; color: #475569; }
img { border-radius: 6px; border: 1px solid #ddd; max-height: 120px; }
@media print {
body { padding: 0; }
button { display: none; }
}
</style>
</head>
<body>
<h1>🎨 Garatujas - Parecer de Traçado Infantil</h1>
<div class="meta-box">
<img src="${activeGaratujaReport.imageUrl}">
<div>
<div><span class="meta-label">Aluno(a):</span> <span>${activeGaratujaReport.alunoName}</span></div>
<div><span class="meta-label">Data de Análise:</span> <span>${new Date(activeGaratujaReport.createdAt).toLocaleDateString('pt-BR')}</span></div>
</div>
</div>
<div>${window.marked ? marked.parse(activeGaratujaReport.analise) : activeGaratujaReport.analise}</div>
<script>
window.onload = function() {
window.print();
setTimeout(() => { window.close(); }, 500);
};
<\/script>
</body>
</html>
`);
printWindow.document.close();
});
}
// Carregar Histórico Garatujas
async function loadGaratujasHistory() {
try {
const response = await fetch('/api/garatujas/list');
if (!response.ok) throw new Error('Erro ao carregar histórico');
const list = await response.json();
garatujasListContainer.innerHTML = '';
if (list.length === 0) {
garatujasNoData.style.display = 'block';
return;
}
garatujasNoData.style.display = 'none';
list.forEach(item => {
const div = document.createElement('div');
div.className = 'quick-note-card';
div.style.cursor = 'pointer';
div.style.display = 'flex';
div.style.flexDirection = 'column';
div.style.gap = '8px';
div.style.padding = '12px';
div.innerHTML = `
<div style="width: 100%; aspect-ratio: 4/3; overflow: hidden; border-radius: 6px; background: rgba(0,0,0,0.2);">
<img src="${item.imageUrl}" style="width: 100%; height: 100%; object-fit: cover;">
</div>
<div style="font-weight: 600; color: var(--text-primary); font-size: 0.9rem;">${escapeHtml(item.alunoName)}</div>
<div style="font-size: 0.75rem; color: var(--text-muted);">${new Date(item.createdAt).toLocaleDateString('pt-BR')}</div>
`;
div.addEventListener('click', async () => {
try {
const detailResp = await fetch(`/api/garatujas/${item.id}`);
if (!detailResp.ok) throw new Error('Erro ao obter detalhes');
const detail = await detailResp.json();
activeGaratujaReport = detail;
garatujasAlunoInput.value = detail.alunoName;
garatujaImgPreview.src = detail.imageUrl;
garatujaPreviewContainer.style.display = 'block';
garatujaReportArea.innerHTML = window.marked ? marked.parse(detail.analise) : detail.analise;
garatujaResultArea.style.display = 'flex';
if (tabGaratujasNova) tabGaratujasNova.click();
} catch (e) {
alert('Falha ao abrir desenho.');
}
});
garatujasListContainer.appendChild(div);
});
} catch (err) {
console.error(err);
}
}
// ============================================================
// BRINCARATIVO (CIRCUITOS PSICOMOTORES E BRINCADEIRAS)
// ============================================================
const brincarAtivoModal = document.getElementById('brincarAtivoModal');
const btnCloseBrincarAtivoModal = document.getElementById('btnCloseBrincarAtivoModal');
const barBtnBrincarAtivo = document.getElementById('barBtnBrincarAtivo');
const tabBrincarAtivoNova = document.getElementById('tabBrincarAtivoNova');
const tabBrincarAtivoHistorico = document.getElementById('tabBrincarAtivoHistorico');
const contentBrincarAtivoNova = document.getElementById('contentBrincarAtivoNova');
const contentBrincarAtivoHistorico = document.getElementById('contentBrincarAtivoHistorico');
const brincarAtivoFaixaSelect = document.getElementById('brincarAtivoFaixaSelect');
const brincarAtivoRecursosInput = document.getElementById('brincarAtivoRecursosInput');
const btnGenerateBrincarAtivo = document.getElementById('btnGenerateBrincarAtivo');
const brincarAtivoLoader = document.getElementById('brincarAtivoLoader');
const brincarAtivoLoaderText = document.getElementById('brincarAtivoLoaderText');
const brincarAtivoResultArea = document.getElementById('brincarAtivoResultArea');
const brincarAtivoReportArea = document.getElementById('brincarAtivoReportArea');
const btnCopyBrincarAtivoReport = document.getElementById('btnCopyBrincarAtivoReport');
const btnPdfBrincarAtivoReport = document.getElementById('btnPdfBrincarAtivoReport');
const brincarAtivoListContainer = document.getElementById('brincarAtivoListContainer');
const brincarAtivoNoData = document.getElementById('brincarAtivoNoData');
let activeBrincarAtivoReport = null;
// Abas do BrincarAtivo
if (tabBrincarAtivoNova) {
tabBrincarAtivoNova.addEventListener('click', () => {
tabBrincarAtivoNova.classList.add('active');
tabBrincarAtivoHistorico.classList.remove('active');
contentBrincarAtivoNova.style.display = 'block';
contentBrincarAtivoHistorico.style.display = 'none';
});
}
if (tabBrincarAtivoHistorico) {
tabBrincarAtivoHistorico.addEventListener('click', () => {
tabBrincarAtivoHistorico.classList.add('active');
tabBrincarAtivoNova.classList.remove('active');
contentBrincarAtivoNova.style.display = 'none';
contentBrincarAtivoHistorico.style.display = 'block';
loadBrincarAtivoHistory();
});
}
// Abrir Modal
if (barBtnBrincarAtivo) {
barBtnBrincarAtivo.addEventListener('click', () => {
brincarAtivoModal.style.display = 'flex';
brincarAtivoRecursosInput.value = '';
brincarAtivoResultArea.style.display = 'none';
brincarAtivoLoader.style.display = 'none';
btnGenerateBrincarAtivo.disabled = false;
activeBrincarAtivoReport = null;
if (tabBrincarAtivoNova) tabBrincarAtivoNova.click();
});
}
// Fechar Modal
if (btnCloseBrincarAtivoModal) {
btnCloseBrincarAtivoModal.addEventListener('click', () => {
brincarAtivoModal.style.display = 'none';
});
}
window.addEventListener('click', (e) => {
if (e.target === brincarAtivoModal) {
brincarAtivoModal.style.display = 'none';
}
});
// Gerar Atividade
if (btnGenerateBrincarAtivo) {
btnGenerateBrincarAtivo.addEventListener('click', async () => {
const faixaEtaria = brincarAtivoFaixaSelect.value;
const recursos = brincarAtivoRecursosInput.value.trim();
if (!recursos) {
alert('Por favor, informe os materiais ou recursos disponíveis.');
return;
}
btnGenerateBrincarAtivo.disabled = true;
brincarAtivoLoader.style.display = 'flex';
brincarAtivoResultArea.style.display = 'none';
brincarAtivoLoaderText.textContent = 'Acessando referências BNCC...';
let statusMsgIdx = 0;
const statusMessages = [
'Analisando faixa etária psicomotora...',
'Estruturando proposta lúdica de circuito...',
'Conectando objetivos específicos da BNCC...',
'Gerando instruções passo a passo para o professor...',
'Finalizando plano de circuito ativo...'
];
const statusInterval = setInterval(() => {
if (statusMsgIdx < statusMessages.length) {
brincarAtivoLoaderText.textContent = statusMessages[statusMsgIdx];
statusMsgIdx++;
} else {
brincarAtivoLoaderText.textContent = 'Formatando atividade...';
}
}, 4500);
try {
const response = await fetch('/api/brincar-ativo/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ faixaEtaria, recursos })
});
clearInterval(statusInterval);
if (!response.ok) {
const errData = await response.json();
throw new Error(errData.error || 'Erro desconhecido');
}
const data = await response.json();
activeBrincarAtivoReport = data;
if (window.marked) {
brincarAtivoReportArea.innerHTML = marked.parse(data.atividade);
} else {
brincarAtivoReportArea.textContent = data.atividade;
}
brincarAtivoLoader.style.display = 'none';
brincarAtivoResultArea.style.display = 'flex';
btnGenerateBrincarAtivo.disabled = false;
} catch (err) {
clearInterval(statusInterval);
console.error(err);
alert('Erro ao gerar atividade: ' + err.message);
brincarAtivoLoader.style.display = 'none';
btnGenerateBrincarAtivo.disabled = false;
}
});
}
// Copiar Parecer BrincarAtivo
if (btnCopyBrincarAtivoReport) {
btnCopyBrincarAtivoReport.addEventListener('click', () => {
if (activeBrincarAtivoReport && activeBrincarAtivoReport.atividade) {
navigator.clipboard.writeText(activeBrincarAtivoReport.atividade)
.then(() => {
btnCopyBrincarAtivoReport.textContent = '✅ Copiado!';
setTimeout(() => { btnCopyBrincarAtivoReport.textContent = '📋 Copiar'; }, 2000);
});
}
});
}
// Baixar PDF BrincarAtivo
if (btnPdfBrincarAtivoReport) {
btnPdfBrincarAtivoReport.addEventListener('click', () => {
if (!activeBrincarAtivoReport) return;
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>BrincarAtivo - Roteiro de Circuito</title>
<style>
body { font-family: 'Outfit', 'Inter', sans-serif; padding: 40px; color: #333; line-height: 1.6; }
h1, h2, h3 { color: #10a37f; margin-top: 24px; margin-bottom: 12px; }
h1 { border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; font-size: 22px; }
.meta-box { background: #f8fafc; padding: 16px; border-radius: 8px; margin-bottom: 24px; border: 1px solid #e2e8f0; }
.meta-label { font-weight: bold; color: #475569; }
@media print {
body { padding: 0; }
button { display: none; }
}
</style>
</head>
<body>
<h1>🏃‍♂️ BrincarAtivo - Planejamento Psicomotor</h1>
<div class="meta-box">
<div><span class="meta-label">Faixa Etária:</span> <span>${activeBrincarAtivoReport.faixaEtaria}</span></div>
<div><span class="meta-label">Recursos:</span> <span>${activeBrincarAtivoReport.recursos}</span></div>
<div><span class="meta-label">Data de Criação:</span> <span>${new Date(activeBrincarAtivoReport.createdAt).toLocaleDateString('pt-BR')}</span></div>
</div>
<div>${window.marked ? marked.parse(activeBrincarAtivoReport.atividade) : activeBrincarAtivoReport.atividade}</div>
<script>
window.onload = function() {
window.print();
setTimeout(() => { window.close(); }, 500);
};
<\/script>
</body>
</html>
`);
printWindow.document.close();
});
}
// Carregar Histórico BrincarAtivo
async function loadBrincarAtivoHistory() {
try {
const response = await fetch('/api/brincar-ativo/list');
if (!response.ok) throw new Error('Erro ao carregar histórico');
const list = await response.json();
brincarAtivoListContainer.innerHTML = '';
if (list.length === 0) {
brincarAtivoNoData.style.display = 'block';
return;
}
brincarAtivoNoData.style.display = 'none';
list.forEach(item => {
const div = document.createElement('div');
div.className = 'quick-note-card';
div.style.cursor = 'pointer';
div.style.display = 'flex';
div.style.flexDirection = 'column';
div.style.gap = '6px';
div.style.padding = '12px';
const lines = item.recursos || '';
const previewText = lines.length > 60 ? lines.substring(0, 60) + '...' : lines;
div.innerHTML = `
<div style="font-weight: 600; color: var(--text-primary); font-size: 0.92rem; display: flex; align-items: center; gap: 6px;">
<span>🏃‍♂️</span> ${escapeHtml(item.faixaEtaria.split(' ')[0])} (${item.faixaEtaria.includes('bem pequenas') ? 'Bem Pequenas' : (item.faixaEtaria.includes('pequenas') ? 'Pequenas' : 'Bebês')})
</div>
<div style="font-size: 0.8rem; color: var(--text-secondary);">Recursos: ${escapeHtml(previewText)}</div>
<div style="font-size: 0.75rem; color: var(--text-muted);">${new Date(item.createdAt).toLocaleDateString('pt-BR')}</div>
`;
div.addEventListener('click', async () => {
try {
const detailResp = await fetch(`/api/brincar-ativo/${item.id}`);
if (!detailResp.ok) throw new Error('Erro ao obter detalhes');
const detail = await detailResp.json();
activeBrincarAtivoReport = detail;
brincarAtivoFaixaSelect.value = detail.faixaEtaria;
brincarAtivoRecursosInput.value = detail.recursos;
brincarAtivoReportArea.innerHTML = window.marked ? marked.parse(detail.atividade) : detail.atividade;
brincarAtivoResultArea.style.display = 'flex';
if (tabBrincarAtivoNova) tabBrincarAtivoNova.click();
} catch (e) {
alert('Falha ao abrir atividade.');
}
});
brincarAtivoListContainer.appendChild(div);
});
} catch (err) {
console.error(err);
}
}
};
// Player de áudio personalizado global para as mídias geradas
+159
View File
@@ -240,6 +240,12 @@
<button type="button" class="btn-prompt-mode mode-video" id="barBtnVideoMind" style="border-color: rgba(6, 182, 212, 0.4); color: #22d3ee; background: rgba(6, 182, 212, 0.05);" title="Análise pedagógica e resumo de vídeos do YouTube">
<span class="mode-icon">🎬</span> VideoMind
</button>
<button type="button" class="btn-prompt-mode mode-image" id="barBtnGaratujas" style="border-color: rgba(249, 115, 22, 0.4); color: #fdba74; background: rgba(249, 115, 22, 0.05);" title="Análise formativa de desenhos e rabiscos infantis">
<span class="mode-icon">🎨</span> Garatujas
</button>
<button type="button" class="btn-prompt-mode mode-text" id="barBtnBrincarAtivo" style="border-color: rgba(16, 163, 127, 0.4); color: #a7f3d0; background: rgba(16, 163, 127, 0.05);" title="Criar circuitos psicomotores e brincadeiras ativas">
<span class="mode-icon">🏃‍♂️</span> BrincarAtivo
</button>
<button type="button" class="btn-prompt-mode mode-music" data-mode="AUDIO" title="Compor e gerar uma música pedagógica">
<span class="mode-icon">🎵</span> Criar Letra de Música
</button>
@@ -1007,6 +1013,159 @@
</div>
</div>
<!-- Modal: Garatujas (Análise de Desenhos) -->
<div id="garatujasModal" class="settings-modal" style="display: none;">
<div class="settings-modal-content" style="max-width: 750px; 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;">🎨 Garatujas - Análise de Traçado Infantil</h3>
<button id="btnCloseGaratujasModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem;">&times;</button>
</div>
<!-- Abas do Modal -->
<div class="settings-tabs" style="padding: 12px 20px 0;">
<button type="button" class="settings-tab active" id="tabGaratujasNova">Nova Análise</button>
<button type="button" class="settings-tab" id="tabGaratujasHistorico">Histórico</button>
</div>
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; flex: 1;">
<!-- CONTEÚDO: NOVA ANÁLISE -->
<div id="contentGaratujasNova" class="tab-content-panel">
<div style="display: flex; flex-direction: column; gap: 16px;">
<!-- NOME DO ALUNO -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Nome do Aluno</label>
<input type="text" id="garatujasAlunoInput" placeholder="Ex: Joãozinho Silva" class="obs-input" style="width: 100%; margin-top: 4px; padding: 10px;">
</div>
<!-- FOTO DO DESENHO -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Foto do Desenho / Rabisco</label>
<div style="display: flex; gap: 12px; align-items: center; margin-top: 6px;">
<button type="button" id="btnUploadGaratuja" class="btn-change-avatar" style="background: rgba(255,255,255,0.02); display: flex; align-items: center; gap: 8px;">
📁 Escolher Imagem
</button>
<input type="file" id="garatujaFileInput" accept="image/*" style="display: none;">
<span id="garatujaFileStatus" style="font-size: 0.85rem; color: var(--text-muted); font-style: italic;">Nenhum arquivo selecionado</span>
</div>
<div id="garatujaPreviewContainer" style="display: none; margin-top: 10px; border: 1px solid var(--border-light); border-radius: 8px; overflow: hidden; max-width: 250px; aspect-ratio: 4/3; background: rgba(0,0,0,0.1);">
<img id="garatujaImgPreview" src="" style="width: 100%; height: 100%; object-fit: contain;">
</div>
</div>
<!-- BOTÃO GERAR -->
<div style="display: flex; flex-direction: column; gap: 10px; margin-top: 8px;">
<button id="btnGenerateGaratuja" style="background: linear-gradient(135deg, #f97316 0%, #ea580c 100%); color: white; border: none; padding: 12px; border-radius: 8px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: opacity 0.2s; box-shadow: 0 4px 12px rgba(249, 115, 22, 0.25);">
<span>🎨 Enviar & Analisar Desenho</span>
</button>
<div id="garatujaLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.9rem; padding: 10px; background: rgba(255, 255, 255, 0.02); border-radius: 8px;">
<div class="record-spinner" style="width: 20px; height: 20px; border-width: 2px; border-top-color: #f97316;"></div>
<span id="garatujaLoaderText">Processando imagem...</span>
</div>
</div>
<!-- ÁREA DE RESULTADO (INICIALMENTE OCULTA) -->
<div id="garatujaResultArea" style="display: none; border-top: 1px solid var(--border-light); padding-top: 16px; flex-direction: column; gap: 18px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4 style="margin: 0; color: var(--text-primary); font-size: 1rem; font-weight: 600;">Laudo de Desenvolvimento</h4>
<div style="display: flex; gap: 8px;">
<button id="btnCopyGaratujaReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer;">📋 Copiar</button>
<button id="btnPdfGaratujaReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer;">📄 PDF</button>
</div>
</div>
<div id="garatujaReportArea" class="report-content-body" style="background: var(--bg-secondary); padding: 16px; border-radius: 10px; max-height: 350px; overflow-y: auto; border: 1px solid var(--border-light); color: var(--text-primary); font-size: 0.95rem; line-height: 1.6;"></div>
</div>
</div>
</div>
<!-- CONTEÚDO: HISTÓRICO -->
<div id="contentGaratujasHistorico" class="tab-content-panel" style="display: none;">
<div id="garatujasListContainer" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; width: 100%;">
<!-- Itens de lista injetados via JS -->
</div>
<div id="garatujasNoData" style="text-align: center; color: var(--text-muted); font-style: italic; padding: 40px 0; width: 100%;">
Nenhum desenho analisado ainda.
</div>
</div>
</div>
</div>
</div>
<!-- Modal: BrincarAtivo (Circuitos e Brincadeiras) -->
<div id="brincarAtivoModal" class="settings-modal" style="display: none;">
<div class="settings-modal-content" style="max-width: 750px; 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;">🏃‍♂️ BrincarAtivo - Fábrica de Brincadeiras & Circuitos</h3>
<button id="btnCloseBrincarAtivoModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem;">&times;</button>
</div>
<!-- Abas do Modal -->
<div class="settings-tabs" style="padding: 12px 20px 0;">
<button type="button" class="settings-tab active" id="tabBrincarAtivoNova">Nova Atividade</button>
<button type="button" class="settings-tab" id="tabBrincarAtivoHistorico">Histórico</button>
</div>
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; flex: 1;">
<!-- CONTEÚDO: NOVA ATIVIDADE -->
<div id="contentBrincarAtivoNova" class="tab-content-panel">
<div style="display: flex; flex-direction: column; gap: 16px;">
<!-- FAIXA ETÁRIA -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Faixa Etária (BNCC)</label>
<select id="brincarAtivoFaixaSelect" class="obs-input" style="width: 100%; margin-top: 4px; padding: 10px; background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border-light); border-radius: 8px;">
<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)">👧 Crianças bem pequenas (1 ano e 7 meses a 3 anos)</option>
<option value="Crianças pequenas (4 anos a 5 anos e 11 meses)">👦 Crianças pequenas (4 anos a 5 anos e 11 meses)</option>
</select>
</div>
<!-- RECURSOS DISPONÍVEIS -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Recursos / Materiais Disponíveis</label>
<textarea id="brincarAtivoRecursosInput" placeholder="Ex: Caixas de papelão, garrafas plásticas vazias, giz para riscar o chão, cones e bambolês." class="obs-input" rows="3" style="width: 100%; margin-top: 4px; padding: 10px; font-family: inherit; resize: vertical;"></textarea>
</div>
<!-- BOTÃO GERAR -->
<div style="display: flex; flex-direction: column; gap: 10px; margin-top: 8px;">
<button id="btnGenerateBrincarAtivo" style="background: linear-gradient(135deg, #10a37f 0%, #0a5f49 100%); color: white; border: none; padding: 12px; border-radius: 8px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: opacity 0.2s; box-shadow: 0 4px 12px rgba(16, 163, 127, 0.25);">
<span>🏃‍♂️ Criar Circuito / Brincadeira</span>
</button>
<div id="brincarAtivoLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.9rem; padding: 10px; background: rgba(255, 255, 255, 0.02); border-radius: 8px;">
<div class="record-spinner" style="width: 20px; height: 20px; border-width: 2px;"></div>
<span id="brincarAtivoLoaderText">Montando planejamento psicomotor...</span>
</div>
</div>
<!-- ÁREA DE RESULTADO (INICIALMENTE OCULTA) -->
<div id="brincarAtivoResultArea" style="display: none; border-top: 1px solid var(--border-light); padding-top: 16px; flex-direction: column; gap: 18px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4 style="margin: 0; color: var(--text-primary); font-size: 1rem; font-weight: 600;">Planejamento Pedagógico</h4>
<div style="display: flex; gap: 8px;">
<button id="btnCopyBrincarAtivoReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer;">📋 Copiar</button>
<button id="btnPdfBrincarAtivoReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer;">📄 PDF</button>
</div>
</div>
<div id="brincarAtivoReportArea" class="report-content-body" style="background: var(--bg-secondary); padding: 16px; border-radius: 10px; max-height: 350px; overflow-y: auto; border: 1px solid var(--border-light); color: var(--text-primary); font-size: 0.95rem; line-height: 1.6;"></div>
</div>
</div>
</div>
<!-- CONTEÚDO: HISTÓRICO -->
<div id="contentBrincarAtivoHistorico" class="tab-content-panel" style="display: none;">
<div id="brincarAtivoListContainer" style="display: flex; flex-direction: column; gap: 12px; width: 100%;">
<!-- Itens de lista injetados via JS -->
</div>
<div id="brincarAtivoNoData" style="text-align: center; color: var(--text-muted); font-style: italic; padding: 40px 0; width: 100%;">
Nenhuma atividade gerada ainda.
</div>
</div>
</div>
</div>
</div>
<!-- Bibliotecas externas para renderizar markdown e blocos de código -->
<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>