Modernizacao completa do Estudio de Cartazes Pedagogicos: correcao de abertura, BNCC, upload de fotos, novo motor de IA, edicao direta e suite de exportacao

This commit is contained in:
2026-09-03 10:41:49 +00:00
parent fe6d392005
commit 1e9d80b69c
5 changed files with 1016 additions and 394 deletions
+550 -203
View File
File diff suppressed because it is too large Load Diff
+229 -97
View File
@@ -2420,109 +2420,181 @@
<!-- MODAL: CRIAR CARTAZES -->
<!-- ========================================== -->
<div id="cartazesModal" class="settings-modal" style="display: none;">
<div class="settings-modal-content" style="max-width: 1100px; width: 95%; max-height: 95vh; 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;">🖼️ Estúdio de Cartazes PedaGog</h3>
<div style="display: flex; gap: 10px;">
<button id="btnCloseCartazesModal" 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: 1180px; width: 96%; max-height: 94vh; display: flex; flex-direction: column; border-radius: 16px; overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5);">
<!-- Cabeçalho do Estúdio de Cartazes -->
<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;">
<div style="display: flex; align-items: center; gap: 10px;">
<div style="background: rgba(16, 185, 129, 0.15); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.3); border-radius: 8px; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem;">
🖼️
</div>
<div>
<h3 style="margin: 0; font-family: 'Outfit', sans-serif; font-size: 1.15rem; color: var(--text-primary); display: flex; align-items: center; gap: 8px;">
Estúdio de Cartazes Pedagógicos
<span style="font-size: 0.68rem; font-weight: 700; background: linear-gradient(135deg, rgba(16,185,129,0.2), rgba(59,130,246,0.2)); color: #34d399; border: 1px solid rgba(16,185,129,0.4); padding: 2px 8px; border-radius: 12px;">BNCC & Design IA</span>
</h3>
<p style="margin: 2px 0 0 0; font-size: 0.76rem; color: var(--text-secondary);">Crie, personalize e imprima cartazes educativos, combinados de rotina e avisos em alta resolução</p>
</div>
</div>
<button id="btnCloseCartazesModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.6rem; padding: 4px; line-height: 1; transition: color 0.2s;" title="Fechar estúdio">&times;</button>
</div>
<!-- Abas do Modal -->
<div style="padding: 12px 20px; display: flex; gap: 15px; border-bottom: 1px solid var(--border-light); background: var(--bg-primary);">
<button id="tabCartazesCriar" class="tab-btn active" style="background: transparent; border: none; color: var(--text-primary); font-weight: 600; padding: 6px 12px; border-radius: 6px; cursor: pointer;">✨ Criar Cartaz</button>
<button id="tabCartazesHistorico" class="tab-btn" style="background: transparent; border: none; color: var(--text-secondary); font-weight: 600; padding: 6px 12px; border-radius: 6px; cursor: pointer;">🕒 Seus Cartazes Salvos</button>
<div style="padding: 10px 20px; display: flex; gap: 10px; border-bottom: 1px solid var(--border-light); background: var(--bg-primary);">
<button id="tabCartazesCriar" class="tab-btn active" style="background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.3); color: #10b981; font-weight: 700; padding: 7px 16px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; gap: 6px; font-size: 0.84rem;">
<span>✨ Criador & Diagramação</span>
</button>
<button id="tabCartazesHistorico" class="tab-btn" style="background: transparent; border: 1px solid transparent; color: var(--text-secondary); font-weight: 600; padding: 7px 16px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; gap: 6px; font-size: 0.84rem;">
<span>🕒 Seus Cartazes Salvos</span>
</button>
</div>
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; flex: 1; display: flex; flex-direction: column;">
<div class="settings-modal-body" style="padding: 18px 20px; overflow-y: auto; flex: 1; display: flex; flex-direction: column; background: var(--bg-secondary);">
<!-- Conteúdo 1: Criador -->
<div id="panelCartazesCriar" style="display: flex; flex-direction: row; gap: 20px; height: 100%; flex-wrap: wrap;">
<div id="panelCartazesCriar" style="display: flex; flex-direction: row; gap: 22px; height: 100%; flex-wrap: wrap;">
<!-- Lado Esquerdo: Configurações e Templates -->
<div id="cartazLeftConfigPanel" style="flex: 1; min-width: 300px; display: flex; flex-direction: column; gap: 14px;">
<!-- Lado Esquerdo: Configurações e Inteligência Pedagógica -->
<div id="cartazLeftConfigPanel" style="flex: 1; min-width: 320px; display: flex; flex-direction: column; gap: 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px dashed var(--border-light); padding-bottom: 8px;">
<h4 style="margin: 0; color: var(--text-primary);">📋 1. Modelo & Conteúdo</h4>
<button type="button" id="btnCartazResetTemplate" class="btn-music-option" style="padding: 2px 8px; font-size: 0.72rem; margin: 0;">Limpar</button>
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 8px;">
<h4 style="margin: 0; color: var(--text-primary); font-size: 0.95rem; display: flex; align-items: center; gap: 6px;">
<span>📋</span> 1. Foco & Estrutura Pedagógica
</h4>
<button type="button" id="btnCartazResetTemplate" class="btn-music-option" style="padding: 3px 10px; font-size: 0.72rem; margin: 0; border-color: rgba(239, 68, 68, 0.3); color: #f87171;">
🔄 Limpar Campos
</button>
</div>
<!-- SELETOR DE TEMPLATES PRÉ-CONFIGURADOS -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Modelos Rápidos (Templates)</label>
<select id="cartazTemplateSelect" class="obs-select" style="width: 100%; margin-top: 4px; font-weight: 600; color: var(--brand-pink, #db2777);">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Modelos Rápidos Pedagógicos</label>
<select id="cartazTemplateSelect" class="obs-select" style="width: 100%; margin-top: 4px; font-weight: 600; color: var(--brand-pink, #db2777); background: var(--bg-tertiary); border: 1px solid rgba(219, 39, 119, 0.3);">
<option value="">✨ Escolha um modelo pedagógico...</option>
<optgroup label="🌟 Educação Infantil (0 a 5 anos)">
<optgroup label="🌟 Educação Infantil (Rotina e Convivência)">
<option value="combinados">🌟 Combinados & Convivência da Turma</option>
<option value="rotina">🍏 Rotina Diária & Lanche Saudável</option>
<option value="aniversarios">🎂 Mural dos Aniversariantes</option>
<option value="brinquedos">🧸 Organização dos Brinquedos e Cantos</option>
<option value="brinquedos">🧸 Organização dos Brinquedos e Cantinhos</option>
</optgroup>
<optgroup label="📚 Campos de Experiência (BNCC)">
<option value="bncc_eu_outro">🤝 O Eu, o Outro e o Nós (EI02EO / EI03EO)</option>
<option value="bncc_corpo">🏃 Corpo, Gestos e Movimentos (EI02CG / EI03CG)</option>
<option value="bncc_tracos">🎨 Traços, Sons, Cores e Formas (EI02TS / EI03TS)</option>
<option value="bncc_escuta">📖 Escuta, Fala e Imaginação (EI02EF / EI03EF)</option>
<option value="bncc_espacos">🔍 Espaços, Tempos e Relações (EI02ET / EI03ET)</option>
<option value="bncc_escuta">📖 Escuta, Fala, Pensamento e Imaginação (EI02EF)</option>
<option value="bncc_espacos">🔍 Espaços, Tempos, Quantidades e Relações (EI02ET)</option>
</optgroup>
<optgroup label="🧠 Metodologia Mind Lab">
<optgroup label="🧠 Metodologia Mind Lab & Jogos">
<option value="mindlab_regras">♟️ Cantinho dos Jogos & Pensamento Estratégico</option>
<option value="mindlab_cooperacao">🧩 Desafios de Raciocínio & Trabalho em Equipe</option>
</optgroup>
<optgroup label="📢 Avisos & Famílias">
<optgroup label="📢 Avisos, Família & Comunidade">
<option value="aviso_pais">📌 Recado Importante para as Famílias</option>
<option value="mostra_cultural">🎉 Convite para Mostra Pedagógica & Cultural</option>
</optgroup>
</select>
</div>
<!-- VINCULAR TURMA -->
<!-- VINCULAR TURMA E FORMATO -->
<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.75rem; font-weight: 600; text-transform: uppercase;">Vincular à Turma</label>
<div class="settings-group" style="flex: 1.2; min-width: 140px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Turma Escolar</label>
<select id="cartazTurmaSelect" 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.75rem; font-weight: 600; text-transform: uppercase;">Orientação</label>
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Formato / Papel</label>
<select id="cartazOrientacaoSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="retrato" selected>📄 Retrato (Vertical A4)</option>
<option value="paisagem">📃 Paisagem (Horizontal A4)</option>
<option value="retrato" selected>📄 Retrato (A4 Vertical)</option>
<option value="paisagem">📃 Paisagem (A4 Horizontal)</option>
<option value="quadrado">📱 Quadrado (1:1 WhatsApp/Post)</option>
</select>
</div>
</div>
<!-- FAIXA ETÁRIA E CAMPO BNCC -->
<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="cartazFaixaEtaria" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Bebês e Bem Pequenas (0 a 3 anos)">Berçário & Maternal (0 a 3 anos)</option>
<option value="Crianças Pequenas (4 a 5 anos e 11 meses)" selected>Pré-Escola (4 e 5 anos)</option>
<option value="Ensino Fundamental I">Ensino Fundamental (Anos Iniciais)</option>
<option value="Geral da Escola">Toda a Comunidade Escolar</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;">Eixo BNCC / Tom</label>
<select id="cartazCampoBncc" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="Convivência e Interações">🤝 Convivência & Afeto</option>
<option value="Rotina e Autonomia">🍏 Rotina & Autonomia</option>
<option value="Linguagem e Oralidade">📖 Letramento & Histórias</option>
<option value="Corpo e Movimento">🏃 Movimento & Expressão</option>
<option value="Artes e Expressão">🎨 Arte, Sons e Cores</option>
<option value="Investigação da Natureza">🌱 Natureza & Ciências</option>
<option value="Avisos às Famílias">📌 Avisos & Comunicados</option>
</select>
</div>
</div>
<!-- CAMPOS EDITÁVEIS EM TEMPO REAL -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Título Principal</label>
<input type="text" id="cartazTituloInput" placeholder="Ex: Combinados da Nossa Turma" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Título Principal do Cartaz</label>
<input type="text" id="cartazTituloInput" placeholder="Ex: Combinados da Nossa Turma" class="obs-input" style="width: 100%; margin-top: 4px; padding: 9px; font-weight: 700; font-size: 0.95rem;">
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div class="settings-group" style="flex: 1.3; min-width: 160px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Subtítulo / Descrição</label>
<input type="text" id="cartazSubtituloInput" placeholder="Ex: Educação Infantil • Professora Camila" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
</div>
<div class="settings-group" style="flex: 0.8; min-width: 110px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Etiqueta / Badge</label>
<input type="text" id="cartazBadgeInput" placeholder="Ex: 🌟 BNCC EI02" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
</div>
</div>
<!-- DESTAQUES / ITENS DO CARTAZ COM EMOJIS RÁPIDOS -->
<div class="settings-group">
<div style="display: flex; justify-content: space-between; align-items: center;">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Itens & Destaques (um por linha)</label>
<button type="button" id="btnCartazAddLine" style="background: none; border: none; color: #10b981; font-size: 0.72rem; font-weight: 700; cursor: pointer; padding: 2px 6px;">+ Adicionar Linha</button>
</div>
<textarea id="cartazCardsInput" placeholder="Digite os itens ou mensagens do cartaz (um por linha)..." class="obs-input" style="width: 100%; min-height: 90px; padding: 9px; margin-top: 4px; font-size: 0.84rem; resize: vertical; line-height: 1.4;"></textarea>
<!-- Atalhos rápidos de emojis -->
<div style="display: flex; gap: 4px; flex-wrap: wrap; margin-top: 4px; align-items: center;">
<span style="font-size: 0.68rem; color: var(--text-muted); margin-right: 4px;">Inserir:</span>
<button type="button" class="btn-emoji-insert" data-emoji="🤝" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🤝</button>
<button type="button" class="btn-emoji-insert" data-emoji="🌟" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🌟</button>
<button type="button" class="btn-emoji-insert" data-emoji="🧸" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🧸</button>
<button type="button" class="btn-emoji-insert" data-emoji="🧼" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🧼</button>
<button type="button" class="btn-emoji-insert" data-emoji="🍎" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🍎</button>
<button type="button" class="btn-emoji-insert" data-emoji="🎨" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🎨</button>
<button type="button" class="btn-emoji-insert" data-emoji="📚" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">📚</button>
<button type="button" class="btn-emoji-insert" data-emoji="🎵" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🎵</button>
<button type="button" class="btn-emoji-insert" data-emoji="🌿" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🌿</button>
<button type="button" class="btn-emoji-insert" data-emoji="🧩" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">🧩</button>
<button type="button" class="btn-emoji-insert" data-emoji="💖" style="background: rgba(255,255,255,0.06); border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 5px; cursor: pointer;">💖</button>
</div>
</div>
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Subtítulo / Faixa Etária</label>
<input type="text" id="cartazSubtituloInput" placeholder="Ex: Educação Infantil • Professora Camila" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
</div>
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Conteúdo / Destaques do Cartaz</label>
<textarea id="cartazCardsInput" placeholder="Digite os itens ou mensagens do cartaz (um por linha)..." class="obs-input" style="width: 100%; min-height: 80px; padding: 8px; margin-top: 4px; font-size: 0.82rem; resize: vertical;"></textarea>
</div>
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Rodapé / Mensagem de Apoio</label>
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Rodapé / Mensagem Institucional</label>
<input type="text" id="cartazRodapeInput" placeholder="Ex: EMEI São Bernardo do Campo • Projeto 2026" class="obs-input" style="width: 100%; margin-top: 4px; padding: 8px;">
</div>
<!-- ESTILO, CORES E FONTES -->
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 8px; margin-top: 4px;">🎨 2. Estilo e Cores</h4>
<h4 style="margin: 6px 0 0 0; color: var(--text-primary); font-size: 0.95rem; border-bottom: 1px solid var(--border-light); padding-bottom: 8px; display: flex; align-items: center; gap: 6px;">
<span>🎨</span> 2. Estilo Visual & Paletas
</h4>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div class="settings-group" style="flex: 1; min-width: 130px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Fonte Lúdica</label>
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Tipografia</label>
<select id="cartazFontSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="'Fredoka', sans-serif" selected>🎈 Fredoka (Fofa/Infantil)</option>
<option value="'Outfit', sans-serif">⚡ Outfit (Moderna)</option>
<option value="'Outfit', sans-serif">⚡ Outfit (Moderna/Limpa)</option>
<option value="'Caveat', cursive">✍️ Caveat (Manuscrita)</option>
<option value="'Playfair Display', serif">📖 Playfair (Clássica)</option>
</select>
@@ -2534,6 +2606,16 @@
<option value="6px dashed" selected>Pontilhada Colorida</option>
<option value="6px double">Linha Dupla Clássica</option>
<option value="8px solid">Moldura de Gibi</option>
<option value="4px solid">Borda Fina Delicada</option>
</select>
</div>
<div class="settings-group" style="flex: 1; min-width: 130px;">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Estilo dos Cartões</label>
<select id="cartazCardStyleSelect" class="obs-select" style="width: 100%; margin-top: 4px;">
<option value="glass" selected>Transparente Suave (Glass)</option>
<option value="solid">Branco Sólido com Sombra</option>
<option value="postit">Estilo Post-it Colorido</option>
<option value="minimal">Borda Minimalista</option>
</select>
</div>
</div>
@@ -2542,40 +2624,60 @@
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.72rem; font-weight: 600; text-transform: uppercase;">Paletas Pedagógicas</label>
<div style="display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap;">
<button type="button" class="btn-palette-preset" data-bg="#fff9eb" data-text="#2d3748" data-card="#ffffff" style="background: #fff9eb; color: #2d3748; padding: 4px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Tons Pastéis</button>
<button type="button" class="btn-palette-preset" data-bg="#f0fdf4" data-text="#166534" data-card="#ffffff" style="background: #f0fdf4; color: #166534; padding: 4px 8px; border: 1px solid #bbf7d0; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Jardim Verde</button>
<button type="button" class="btn-palette-preset" data-bg="#eff6ff" data-text="#1e40af" data-card="#ffffff" style="background: #eff6ff; color: #1e40af; padding: 4px 8px; border: 1px solid #bfdbfe; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Céu Azul</button>
<button type="button" class="btn-palette-preset" data-bg="#fdf2f8" data-text="#9d174d" data-card="#ffffff" style="background: #fdf2f8; color: #9d174d; padding: 4px 8px; border: 1px solid #fbcfe8; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Rosa Lúdico</button>
<button type="button" class="btn-palette-preset" data-bg="#1e293b" data-text="#f8fafc" data-card="#334155" style="background: #1e293b; color: #f8fafc; padding: 4px 8px; border: 1px solid #475569; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold;">Lousa Escura</button>
<button type="button" class="btn-palette-preset" data-bg="#fff9eb" data-text="#2d3748" data-card="#ffffff" style="background: #fff9eb; color: #2d3748; padding: 5px 9px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Tons Pastéis</button>
<button type="button" class="btn-palette-preset" data-bg="#f0fdf4" data-text="#166534" data-card="#ffffff" style="background: #f0fdf4; color: #166534; padding: 5px 9px; border: 1px solid #bbf7d0; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Jardim Verde</button>
<button type="button" class="btn-palette-preset" data-bg="#eff6ff" data-text="#1e40af" data-card="#ffffff" style="background: #eff6ff; color: #1e40af; padding: 5px 9px; border: 1px solid #bfdbfe; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Céu Azul</button>
<button type="button" class="btn-palette-preset" data-bg="#fdf2f8" data-text="#9d174d" data-card="#ffffff" style="background: #fdf2f8; color: #9d174d; padding: 5px 9px; border: 1px solid #fbcfe8; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Rosa Lúdico</button>
<button type="button" class="btn-palette-preset" data-bg="#fef3c7" data-text="#92400e" data-card="#ffffff" style="background: #fef3c7; color: #92400e; padding: 5px 9px; border: 1px solid #fde68a; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Sol da Manhã</button>
<button type="button" class="btn-palette-preset" data-bg="#1e293b" data-text="#f8fafc" data-card="#334155" style="background: #1e293b; color: #f8fafc; padding: 5px 9px; border: 1px solid #475569; border-radius: 6px; font-size: 0.72rem; cursor: pointer; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">Lousa Escura</button>
</div>
<div style="display: flex; gap: 12px; margin-top: 8px; align-items: center;">
<div style="display: flex; align-items: center; gap: 6px;">
<label style="font-size: 0.75rem; color: var(--text-secondary);">Fundo:</label>
<input type="color" id="cartazBgColor" value="#fff9eb" style="width: 28px; height: 28px; border: 1px solid var(--border-light); border-radius: 6px; cursor: pointer; background: transparent;">
</div>
<div style="display: flex; align-items: center; gap: 6px;">
<label style="font-size: 0.75rem; color: var(--text-secondary);">Texto:</label>
<input type="color" id="cartazTextColor" value="#2d3748" style="width: 28px; height: 28px; border: 1px solid var(--border-light); border-radius: 6px; cursor: pointer; background: transparent;">
</div>
</div>
</div>
<!-- ILUSTRAÇÃO POR IA -->
<div class="settings-group">
<label style="color: var(--text-secondary); font-size: 0.75rem; font-weight: 600; text-transform: uppercase;">Ilustração por IA (MiniMax T2I)</label>
<textarea id="cartazPromptImagem" placeholder="Ex: Ilustração 3D Pixar de crianças felizes brincando juntas na escola, cores vivas..." class="obs-input" style="width: 100%; min-height: 50px; padding: 6px; margin-top: 4px; font-size: 0.78rem; resize: vertical;"></textarea>
<div style="display: flex; gap: 8px; margin-top: 6px;">
<button type="button" id="btnGenerateCartazImage" class="btn-music-option" style="border-color: #8b5cf6; color: #a78bfa; background: rgba(139, 92, 246, 0.1); font-weight: 600; padding: 5px 10px; font-size: 0.78rem;">
<span>🪄 Gerar Ilustração com IA</span>
<!-- ILUSTRAÇÃO & IMAGEM (IA OU FOTO PRÓPRIA) -->
<div class="settings-group" style="background: rgba(255,255,255,0.02); border: 1px solid var(--border-light); border-radius: 10px; padding: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<label style="color: var(--text-primary); font-size: 0.78rem; font-weight: 700; text-transform: uppercase;">🖼️ Ilustração do Cartaz</label>
<span style="font-size: 0.7rem; color: var(--text-muted);">IA Pixar ou Foto Própria</span>
</div>
<textarea id="cartazPromptImagem" placeholder="Descreva a cena desejada (ex: Crianças de diferentes origens brincando juntas em roda no jardim florido)..." class="obs-input" style="width: 100%; min-height: 52px; padding: 7px; font-size: 0.8rem; resize: vertical;"></textarea>
<div style="display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap;">
<button type="button" id="btnGenerateCartazImage" class="btn-music-option" style="border-color: #8b5cf6; color: #a78bfa; background: rgba(139, 92, 246, 0.12); font-weight: 600; padding: 6px 12px; font-size: 0.8rem;">
<span>🪄 Gerar com IA</span>
</button>
<button type="button" id="btnRemoveCartazImage" class="btn-music-option" style="color: var(--text-muted); font-size: 0.78rem; padding: 5px 10px;">
Remover Imagem
<label class="btn-music-option" style="border-color: #3b82f6; color: #60a5fa; background: rgba(59, 130, 246, 0.12); font-weight: 600; padding: 6px 12px; font-size: 0.8rem; cursor: pointer; margin: 0; display: inline-flex; align-items: center; gap: 4px;">
<span>📷 Usar Foto Própria</span>
<input type="file" id="cartazImageUploadInput" accept="image/*" style="display: none;">
</label>
<button type="button" id="btnRemoveCartazImage" class="btn-music-option" style="color: var(--text-muted); font-size: 0.78rem; padding: 6px 10px; margin-left: auto;">
🗑️ Remover
</button>
</div>
</div>
<!-- BOTÃO GERAR NOVO CARTAZ COM IA COMPLETA -->
<button type="button" id="btnGenerateCartaz" style="width: 100%; padding: 11px; background: #10b981; 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 12px rgba(16, 185, 129, 0.3); transition: all 0.2s; margin-top: 4px;">
<button type="button" id="btnGenerateCartaz" style="width: 100%; padding: 13px; background: linear-gradient(135deg, #10b981, #059669); color: white; border: none; border-radius: 12px; font-weight: 700; font-size: 0.96rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.35); transition: all 0.2s; margin-top: 6px;">
<span>🤖 Redigir Conteúdo com PedagogIA</span>
</button>
<div id="cartazGenerateLoader" 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 id="cartazGenerateLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.85rem; padding: 12px; background: rgba(16, 185, 129, 0.05); border: 1px solid rgba(16, 185, 129, 0.2); border-radius: 8px;">
<div class="media-spinner" style="width: 18px; height: 18px; border-width: 2px; border-top-color: #10b981;"></div>
<span id="cartazLoaderText">Diagramando cartaz pedagógico...</span>
<span id="cartazLoaderText">Diagramando cartaz com inteligência pedagógica...</span>
</div>
</div>
<!-- Lado Esquerdo: Painel de Assets para Canvas -->
<!-- Lado Esquerdo: Painel de Assets para Canvas (Modo Interativo) -->
<div id="cartazLeftCanvasPanel" style="display: none; flex: 1; min-width: 320px; flex-direction: column; gap: 16px; padding-right: 12px;">
<h4 style="margin: 0; color: var(--text-primary); border-bottom: 1px dashed var(--border-light); padding-bottom: 8px;">🎨 Elementos do Canvas</h4>
@@ -2615,7 +2717,6 @@
</div>
<div id="sceneDecomposeStatus" style="font-size: 0.78rem; color: var(--text-secondary); text-align: center;">Gerando camadas...</div>
</div>
</div>
<div class="settings-group">
@@ -2635,29 +2736,49 @@
</div>
</div>
<!-- Lado Direito: Preview & Editor de Código -->
<div class="cartaz-preview-panel" style="flex: 1.3; min-width: 360px; display: flex; flex-direction: column; gap: 14px;">
<div class="cartaz-header-container" style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 8px; flex-wrap: wrap; gap: 8px;">
<h4 style="margin: 0; color: var(--text-primary);">👁️ Visualização em Tempo Real</h4>
<div class="cartaz-header-actions" style="display: flex; gap: 6px; flex-wrap: wrap;">
<button type="button" id="btnExportCartazPNG" class="btn-save-settings" style="background: #10b981; border: none; color: white; padding: 5px 10px; font-size: 0.75rem; font-weight: 700; border-radius: 6px; cursor: pointer;" title="Baixar imagem PNG de alta resolução">🖼️ Baixar PNG</button>
<button type="button" id="btnExportCartazPDF" class="btn-save-settings" style="background: #8b5cf6; border: none; color: white; padding: 5px 10px; font-size: 0.75rem; font-weight: 700; border-radius: 6px; cursor: pointer;" title="Baixar documento PDF pronto para impressão">📄 Baixar PDF</button>
<button type="button" id="btnPrintCartaz" class="btn-save-settings" style="background: #3b82f6; border: none; color: white; padding: 5px 10px; font-size: 0.75rem; font-weight: 700; border-radius: 6px; cursor: pointer;">🖨️ Imprimir</button>
<button type="button" id="btnCanvasModeToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 8px; font-size: 0.75rem;">🎨 Canvas</button>
<button type="button" id="btnEditCartazToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 8px; font-size: 0.75rem;">✏️ HTML</button>
<!-- Lado Direito: Preview & Suite de Exportação de Alta Resolução -->
<div class="cartaz-preview-panel" style="flex: 1.4; min-width: 380px; display: flex; flex-direction: column; gap: 12px;">
<div class="cartaz-header-container" style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); padding-bottom: 10px; flex-wrap: wrap; gap: 10px;">
<div>
<h4 style="margin: 0; color: var(--text-primary); font-size: 0.95rem; display: flex; align-items: center; gap: 6px;">
<span>👁️</span> Visualização do Cartaz
</h4>
<span style="font-size: 0.72rem; color: var(--text-secondary);">Clique diretamente no texto para editar</span>
</div>
<!-- AÇÕES DE EXPORTAÇÃO E FERRAMENTAS -->
<div class="cartaz-header-actions" style="display: flex; gap: 6px; flex-wrap: wrap; align-items: center;">
<button type="button" id="btnCopyCartazClipboard" class="btn-save-settings" style="background: rgba(16, 185, 129, 0.15); border: 1px solid rgba(16, 185, 129, 0.4); color: #10b981; padding: 6px 10px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer;" title="Copiar imagem para colar direto no WhatsApp, Word ou Canva">
📋 Copiar Imagem
</button>
<button type="button" id="btnExportCartazPNG" class="btn-save-settings" style="background: #10b981; border: none; color: white; padding: 6px 11px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer; box-shadow: 0 2px 6px rgba(16, 185, 129, 0.3);" title="Baixar imagem PNG de alta resolução">
🖼️ PNG
</button>
<button type="button" id="btnExportCartazPDF" class="btn-save-settings" style="background: #8b5cf6; border: none; color: white; padding: 6px 11px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer; box-shadow: 0 2px 6px rgba(139, 92, 246, 0.3);" title="Baixar documento PDF pronto para impressão">
📄 PDF
</button>
<button type="button" id="btnPrintCartaz" class="btn-save-settings" style="background: #3b82f6; border: none; color: white; padding: 6px 11px; font-size: 0.76rem; font-weight: 700; border-radius: 8px; cursor: pointer;" title="Abrir janela de impressão">
🖨️ Imprimir
</button>
<button type="button" id="btnCanvasModeToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 6px 9px; font-size: 0.76rem; border-radius: 8px;" title="Alternar modo de design livre com arrastar e soltar">
🎨 Canvas
</button>
<button type="button" id="btnEditCartazToggle" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 6px 9px; font-size: 0.76rem; border-radius: 8px;" title="Ver ou editar código HTML">
✏️ HTML
</button>
</div>
</div>
<!-- Painel de Controle do Canvas (Apenas visível no Modo Canvas) -->
<div id="canvasControlPanel" style="display: none; background: var(--bg-secondary); border: 1px solid var(--border-light); padding: 10px; border-radius: 8px; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 8px;">
<button id="btnCanvasAddText" class="btn-save-settings" style="background: #10a37f; color: white; border: none; padding: 4px 10px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;"> Texto</button>
<button id="btnCanvasAddCard" class="btn-save-settings" style="background: #a855f7; color: white; border: none; padding: 4px 10px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;"> Card</button>
<div id="canvasControlPanel" style="display: none; background: var(--bg-tertiary); border: 1px solid var(--border-light); padding: 10px 14px; border-radius: 10px; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 6px;">
<button id="btnCanvasAddText" class="btn-save-settings" style="background: #10a37f; color: white; border: none; padding: 5px 12px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;"> Texto</button>
<button id="btnCanvasAddCard" class="btn-save-settings" style="background: #a855f7; color: white; border: none; padding: 5px 12px; font-size: 0.75rem; font-weight: 600; border-radius: 6px; cursor: pointer;"> Card</button>
<div style="height: 20px; width: 1px; background: var(--border-light);"></div>
<!-- Controles do Item Selecionado -->
<span style="font-size: 0.75rem; color: var(--text-secondary);">Item:</span>
<select id="canvasFontFamily" class="obs-select" style="padding: 2px 5px; font-size: 0.75rem; min-width: 90px; margin: 0;">
<select id="canvasFontFamily" class="obs-select" style="padding: 3px 6px; font-size: 0.75rem; min-width: 90px; margin: 0;">
<option value="Fredoka">Fredoka</option>
<option value="Outfit">Outfit</option>
<option value="Caveat">Caveat</option>
@@ -2665,7 +2786,7 @@
<option value="Arial">Arial</option>
</select>
<input type="number" id="canvasFontSize" class="obs-input" style="width: 55px; padding: 2px 5px; font-size: 0.75rem; margin: 0;" min="8" max="72" value="16" title="Tamanho da fonte (px)">
<input type="number" id="canvasFontSize" class="obs-input" style="width: 55px; padding: 3px 6px; font-size: 0.75rem; margin: 0;" min="8" max="72" value="16" title="Tamanho da fonte (px)">
<div style="display: flex; align-items: center; gap: 4px;">
<label style="font-size: 0.72rem; color: var(--text-secondary);">Cor:</label>
@@ -2688,45 +2809,56 @@
</div>
<div style="display: flex; gap: 4px; align-items: center;">
<button id="btnCanvasZIndexUp" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 2px 6px; font-size: 0.7rem; cursor: pointer;" title="Trazer para Frente">🔺</button>
<button id="btnCanvasZIndexDown" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 2px 6px; font-size: 0.7rem; cursor: pointer;" title="Enviar para Trás">🔻</button>
<button id="btnCanvasDelete" class="btn-save-settings" style="background: #ef4444; color: white; border: none; padding: 2px 8px; font-size: 0.7rem; font-weight: bold; border-radius: 4px; cursor: pointer;" title="Excluir Elemento">🗑️ Excluir</button>
<button id="btnCanvasZIndexUp" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 3px 7px; font-size: 0.7rem; cursor: pointer;" title="Trazer para Frente">🔺</button>
<button id="btnCanvasZIndexDown" class="btn-save-settings" style="background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 3px 7px; font-size: 0.7rem; cursor: pointer;" title="Enviar para Trás">🔻</button>
<button id="btnCanvasDelete" class="btn-save-settings" style="background: #ef4444; color: white; border: none; padding: 3px 8px; font-size: 0.7rem; font-weight: bold; border-radius: 4px; cursor: pointer;" title="Excluir Elemento">🗑️</button>
</div>
</div>
<!-- Área de Edição do Conteúdo (Inicialmente Oculta) -->
<!-- Área de Edição de Código HTML (Opcional) -->
<div id="cartazEditArea" style="display: none; flex-direction: column; gap: 8px;">
<label style="color: var(--text-secondary); font-size: 0.78rem; font-weight: 600; text-transform: uppercase;">Editor HTML do Cartaz</label>
<label style="color: var(--text-secondary); font-size: 0.78rem; font-weight: 600; text-transform: uppercase;">Editor HTML Direto</label>
<textarea id="cartazHtmlEditor" class="obs-input" style="width: 100%; min-height: 120px; font-family: monospace; font-size: 0.85rem; padding: 10px;"></textarea>
<button type="button" id="btnUpdateCartazPreview" style="padding: 6px 12px; align-self: flex-end; background: var(--border-light); border: 1px solid var(--border-light); border-radius: 6px; cursor: pointer; color: var(--text-primary); font-size: 0.8rem;">Atualizar Preview</button>
<button type="button" id="btnUpdateCartazPreview" style="padding: 6px 14px; align-self: flex-end; background: #10b981; border: none; border-radius: 6px; cursor: pointer; color: white; font-weight: 700; font-size: 0.8rem;">Atualizar Preview</button>
</div>
<!-- Caixa de Preview Imprimível -->
<div style="background: #2d3748; padding: 12px; border-radius: 12px; border: 1px solid var(--border-light); overflow: auto; display: flex; justify-content: center; align-items: center; flex: 1; min-height: 400px;">
<div id="cartazPrintableArea" style="background: #fff9eb; color: #2d3748; padding: 30px; border-radius: 12px; width: 100%; max-width: 500px; box-sizing: border-box; box-shadow: 0 10px 25px rgba(0,0,0,0.3); transition: all 0.3s; min-height: 600px; display: flex; flex-direction: column; font-family: 'Fredoka', sans-serif;">
<h1 id="cartazPreviewTitle" style="text-align: center; margin-top: 0; font-size: 2.2rem; border-bottom: 2px solid; padding-bottom: 12px; margin-bottom: 20px;">Título do Cartaz</h1>
<!-- Caixa de Preview Imprimível em Escala Real -->
<div id="cartazPreviewScrollContainer" style="background: #0f172a; padding: 24px; border-radius: 14px; border: 1px solid var(--border-light); overflow: auto; display: flex; justify-content: center; align-items: flex-start; flex: 1; min-height: 520px; max-height: 70vh;">
<div id="cartazPrintableArea" style="background: #fff9eb; color: #2d3748; padding: 32px 28px; border-radius: 12px; width: 100%; max-width: 500px; box-sizing: border-box; box-shadow: 0 15px 35px rgba(0,0,0,0.4); transition: all 0.3s; min-height: 620px; display: flex; flex-direction: column; font-family: 'Fredoka', sans-serif; position: relative;">
<!-- Área da Imagem IA no Cartaz -->
<div id="cartazPreviewImageContainer" style="display: none; width: 100%; margin-bottom: 20px; border-radius: 8px; border: 2px solid rgba(0,0,0,0.1); background: rgba(0,0,0,0.02); text-align: center; overflow: hidden;">
<img id="cartazPreviewImage" src="" style="max-width: 100%; max-height: 240px; object-fit: contain; display: inline-block; vertical-align: middle;">
<!-- Selo / Badge Superior (Opcional) -->
<div id="cartazPreviewBadge" style="display: none; align-self: center; margin-bottom: 12px; background: rgba(0,0,0,0.06); border: 1px solid rgba(0,0,0,0.12); padding: 3px 12px; border-radius: 20px; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.4px;">
🌟 BNCC • EDUCAÇÃO INFANTIL
</div>
<!-- Título do Cartaz (Com suporte a edição direta no preview) -->
<h1 id="cartazPreviewTitle" contenteditable="true" spellcheck="false" style="text-align: center; margin-top: 0; font-size: 2.2rem; border-bottom: 2px solid; padding-bottom: 12px; margin-bottom: 18px; outline: none; transition: background 0.2s;" title="Clique para editar o título diretamente">Título do Cartaz</h1>
<!-- Área da Imagem no Cartaz -->
<div id="cartazPreviewImageContainer" style="display: none; width: 100%; margin-bottom: 18px; border-radius: 10px; border: 2px solid rgba(0,0,0,0.1); background: rgba(0,0,0,0.02); text-align: center; overflow: hidden; position: relative;">
<img id="cartazPreviewImage" src="" style="max-width: 100%; max-height: 250px; object-fit: contain; display: inline-block; vertical-align: middle;">
</div>
<!-- Conteúdo de Texto do Cartaz -->
<div id="cartazPreviewContent" style="font-size: 1.1rem; line-height: 1.6; flex: 1; display: flex; flex-direction: column; gap: 12px;">
<p style="text-align: center; color: #718096; font-style: italic;">Clique em "Criar Cartaz Pedagógico" para que a assistente monte e estruture o cartaz ideal para você.</p>
<div id="cartazPreviewContent" style="font-size: 1.1rem; line-height: 1.6; flex: 1; display: flex; flex-direction: column; gap: 12px; outline: none;">
<p style="text-align: center; color: #718096; font-style: italic; padding: 40px 10px;">
Clique em <strong>"Redigir Conteúdo com PedagogIA"</strong> ou escolha um <strong>Modelo Rápido</strong> ao lado para ver o cartaz diagramado instantaneamente.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Conteúdo 2: Histórico -->
<!-- Conteúdo 2: Histórico de Cartazes Salvos -->
<div id="panelCartazesHistorico" style="display: none; flex-direction: column; gap: 16px;">
<div id="cartazesHistoryContainer" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px;">
<!-- Preenchido via JS -->
</div>
<div id="cartazesNoData" style="text-align: center; color: var(--text-muted); font-style: italic; padding: 40px 0;">
Nenhum cartaz gerado ainda. Crie um acima!
<div id="cartazesNoData" style="text-align: center; color: var(--text-muted); font-style: italic; padding: 50px 0;">
Nenhum cartaz gerado ainda. Crie seu primeiro cartaz na aba acima!
</div>
</div>
+21 -19
View File
@@ -3917,34 +3917,36 @@ code {
/* Garante que o conteúdo de texto se ajuste */
#cartazPreviewContent {
font-size: 3.5cqw !important;
font-size: 3.2cqw !important;
line-height: 1.4 !important;
flex: 1;
display: flex;
gap: 3cqw;
overflow: hidden;
flex-direction: column;
gap: 2cqw;
overflow: visible;
}
#cartazPreviewContent > div,
#cartazPreviewContent > p,
#cartazPreviewContent > ul {
box-sizing: border-box !important;
padding: 3.5cqw !important;
border-radius: 2cqw !important;
font-size: 3cqw !important;
margin: 0 !important;
#cartazPreviewTitle:focus {
background: rgba(255, 255, 255, 0.25);
outline: 2px dashed rgba(16, 185, 129, 0.8);
border-radius: 8px;
}
#cartazPreviewContent h2,
#cartazPreviewContent h3 {
font-size: 4.5cqw !important;
margin-top: 0 !important;
margin-bottom: 2cqw !important;
.btn-palette-preset {
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
#cartazPreviewContent li {
font-size: 2.8cqw !important;
margin-bottom: 1.5cqw !important;
.btn-palette-preset:hover {
transform: translateY(-2px) scale(1.05);
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.btn-emoji-insert {
transition: transform 0.15s ease;
}
.btn-emoji-insert:hover {
transform: scale(1.2);
}
/* Título principal do cartaz */
+212 -75
View File
@@ -2147,65 +2147,120 @@ app.delete('/api/poesias/:id', requireAuth, async (req, res) => {
// ROTAS DO CRIAR CARTAZES
// ============================================================
app.post('/api/cartazes/generate', requireAuth, async (req, res) => {
const { titulo, tema, corFundo, corTexto, layout, promptImagem, tamanho, orientacao } = req.body;
const {
titulo,
tema,
subtitulo,
rodape,
faixaEtaria,
campoBncc,
tomPedagogico,
turmaId,
corFundo,
corTexto,
layout,
promptImagem,
tamanho,
orientacao,
estiloCards
} = req.body;
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
if (!titulo || !tema || !layout) {
return res.status(400).json({ error: 'Título, tema e layout são obrigatórios.' });
if (!titulo || !tema) {
return res.status(400).json({ error: 'Título e tema/foco são obrigatórios.' });
}
try {
const agentConfig = readAgentConfig();
const agentName = agentConfig.agentName || "PedagogIA";
// 1. Gerar Conteúdo do Cartaz
const posterPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
A professora Camila quer criar um Cartaz para sala de aula da Educação Infantil.
Detalhes do Cartaz:
- Título: "${titulo}"
- Tema / Foco Pedagógico: "${tema}"
- Tipo de Cartaz: "${layout}" (educativo, informativo, conscientizacao, cultural)
- Tamanho: "${tamanho || 'A4'}"
- Orientação: "${orientacao === 'paisagem' ? 'Paisagem/Horizontal' : 'Retrato/Vertical'}"
Por favor, gere o CONTEÚDO textual e estrutural do cartaz em HTML.
REGRAS DE IDIOMA E LOCALIZAÇÃO:
- O conteúdo gerado deve ser IMPRETERIVELMENTE em português do Brasil (pt-BR). Não use termos em outros idiomas de forma desnecessária.
REGRAS FUNDAMENTAIS E ESTÉTICAS DE DIAGRAMAÇÃO:
- O cartaz deve caber ESTRITAMENTE em UMA ÚNICA PÁGINA FÍSICA (Tamanho ${tamanho || 'A4'}, orientação ${orientacao === 'paisagem' ? 'Horizontal/Paisagem' : 'Vertical/Retrato'}). Não extrapole este espaço de forma alguma.
- Limite a quantidade de conteúdo: gere no máximo 2 ou 3 cards (divs de conteúdo) no total. Cada card deve ter pouquíssimo texto (no máximo 3 ou 4 itens curtos por card).
- Distribua a informação em CARDS (pequenos cartões ou blocos curtos) bem espaçados, parecidos com pôsteres infantis modernos, onde o texto NÃO ocupa uma folha corrida.
- Os textos devem ser extremamente curtos, diretos, visuais e fáceis de ler.
- Use emojis apropriados para crianças.
- Se for "educativo", estruture o conteúdo pedagógico de forma visual (ex: caixas para alfabeto, tabuada, regras gramaticais).
- Se for "informativo", destaque avisos gerais e orientações (ex: horários, cardápio, provas) em caixas curtas e legíveis.
- Se for "conscientizacao", use frases curtas de impacto sobre campanhas (ex: prevenção, saúde, coleta seletiva), acompanhadas de dicas em pequenos blocos.
- Se for "cultural", promova a atividade de lazer, esporte ou feira destacando o evento de forma animada e pontual.
- Considere a orientação ${orientacao === 'paisagem' ? 'Paisagem (horizontal)' : 'Retrato (vertical)'} ao organizar o fluxo: para paisagem, os cards devem ficar lado a lado (2 colunas horizontais); para retrato, eles se empilham verticalmente.
- Retorne apenas o formato HTML (use <div>, <p>, <ul>, <li>, <strong>, <span>). Não adicione <html> ou <body>, retorne apenas o conteúdo interno. Não inclua o título <h1> pois ele é colocado no topo pela interface.`;
let conteudoHtml = '';
// Tentar MiniMax M3
try {
console.log('[Cartazes] Gerando conteúdo com MiniMax...');
const result = await callMinimax({
messages: [{ role: 'user', content: posterPrompt }],
temperature: 0.7,
max_tokens: 1500
});
conteudoHtml = result.text;
} catch (err) {
console.error('[Cartazes] Erro MiniMax:', err.message);
// Buscar contexto da turma se fornecido
let turmaContexto = '';
if (turmaId) {
try {
const tRes = await dbPool.query('SELECT nome, ano_letivo, periodo FROM escola.turmas WHERE id = $1', [turmaId]);
if (tRes.rows.length > 0) {
const t = tRes.rows[0];
turmaContexto = `Turma Escolar: "${t.nome}" (${t.ano_letivo || '2026'}, período ${t.periodo || 'integral'})`;
}
} catch (tErr) {
console.warn('[Cartazes] Aviso ao buscar turma:', tErr.message);
}
}
// Fallback OpenRouter
if (!conteudoHtml && process.env.OPENROUTER_API_KEY) {
// 1. Gerar Conteúdo do Cartaz com Inteligência Pedagógica Avançada
const posterPrompt = `Você é a ${agentName}, assistente pedagógica de excelência da professora Camila Martella Gasparini Reifonas, especialista em Educação Infantil e Diretrizes da BNCC.
A professora Camila está criando um Cartaz Pedagógico de alta qualidade para a sala de aula da Educação Infantil.
DIRETRIZES DO PEDIDO:
- Título do Cartaz: "${titulo}"
- Tema / Foco Pedagógico: "${tema}"
- Subtítulo Pretendido: "${subtitulo || ''}"
- Faixa Etária: "${faixaEtaria || 'Educação Infantil (2 a 5 anos)'}"
- Campo de Experiência / BNCC: "${campoBncc || 'Convivência e Aprendizagem'}"
- Tom Pedagógico: "${tomPedagogico || 'Acolhedor, afetuoso e lúdico'}"
${turmaContexto ? `- Contexto: ${turmaContexto}` : ''}
- Formato Físico: Tamanho ${tamanho || 'A4'}, Orientação ${orientacao === 'paisagem' ? 'Paisagem/Horizontal' : 'Retrato/Vertical'}
- Tipo de Layout: "${layout || 'educativo'}" (educativo, combinados, rotina, informativo, conscientizacao, cultural)
REGRAS DE CONTEÚDO E DIAGRAMAÇÃO:
1. IDIOMA: Português do Brasil (pt-BR) impecável, carinhoso, claro e adequado para crianças e famílias.
2. ESPAÇO FÍSICO: O cartaz deve caber perfeitamente em UMA ÚNICA PÁGINA. Portanto, seja sintético, impactante e visual.
3. ESTRUTURA DE CARDS:
- Gere entre 3 a 5 itens/cards principais.
- Cada card DEVE começar com um emoji temático atrativo (ex: 🤝, 🌟, 🧸, 🧼, 🍎, 🎨, 📚).
- Frases curtas, afirmativas, positivas e de fácil memorização pelas crianças.
4. RETORNO OBRIGATÓRIO EM FORMATO JSON:
Responda ESTRITAMENTE com um objeto JSON válido no seguinte formato:
{
"titulo": "Título refinado e encantador para o topo do cartaz",
"subtitulo": "Subtítulo explicativo ou pedagógico (ex: faixa etária / campo BNCC)",
"badge": "Tag ou selo curto (ex: 🌟 BNCC • EI02EO ou 💡 Dica da Semana)",
"cards": [
"🤝 Frase ou combinado 1 com emoji inicial",
"🧸 Frase ou combinado 2 com emoji inicial",
"🧼 Frase ou combinado 3 com emoji inicial",
"👂 Frase ou combinado 4 com emoji inicial"
],
"rodape": "Mensagem carinhosa de fechamento no rodapé",
"promptImagem": "Sugestão detalhada em inglês para ilustrar este cartaz em estilo 3D Pixar ou vetor infantil colorido, SEM NENHUM TEXTO ESCRITO"
}
Não inclua comentários nem texto fora do bloco JSON.`;
let generatedData = null;
let conteudoHtml = '';
// Tentar chamada com MiniMax M3 (ou fallback automático em callMinimax)
try {
console.log('[Cartazes] Gerando cartaz com IA pedagógica...');
const result = await callMinimax({
messages: [{ role: 'user', content: posterPrompt }],
temperature: 0.6,
max_tokens: 1500
});
const rawText = result.text.trim();
// Tentar extrair JSON
const jsonMatch = rawText.match(/\{[\s\S]*\}/);
if (jsonMatch) {
try {
generatedData = JSON.parse(jsonMatch[0]);
} catch (e) {
console.warn('[Cartazes] Erro ao parsear JSON da IA, usando fallback:', e.message);
}
}
if (!generatedData) {
conteudoHtml = rawText.replace(/```html|```/g, '').trim();
}
} catch (err) {
console.error('[Cartazes] Erro ao chamar IA principal:', err.message);
}
// Fallback OpenRouter se necessário
if (!generatedData && !conteudoHtml && process.env.OPENROUTER_API_KEY) {
try {
console.log('[Cartazes] Fallback OpenRouter...');
console.log('[Cartazes] Acionando fallback OpenRouter...');
const r = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
@@ -2215,50 +2270,86 @@ REGRAS FUNDAMENTAIS E ESTÉTICAS DE DIAGRAMAÇÃO:
body: JSON.stringify({
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
messages: [{ role: 'user', content: posterPrompt }],
temperature: 0.7,
temperature: 0.6,
max_tokens: 1500
})
});
if (r.ok) {
const data = await r.json();
conteudoHtml = data.choices?.[0]?.message?.content || '';
const raw = data.choices?.[0]?.message?.content || '';
const match = raw.match(/\{[\s\S]*\}/);
if (match) {
generatedData = JSON.parse(match[0]);
} else {
conteudoHtml = raw.replace(/```html|```/g, '').trim();
}
}
} catch (err) {
console.error('[Cartazes] Erro OpenRouter:', err.message);
console.error('[Cartazes] Erro OpenRouter fallback:', err.message);
}
}
// Se temos JSON estruturado, montamos os campos e o HTML diagramado
const finalTitulo = generatedData?.titulo || titulo;
const finalSubtitulo = generatedData?.subtitulo || subtitulo || tema;
const finalBadge = generatedData?.badge || (campoBncc ? `📚 ${campoBncc}` : '');
const finalCards = Array.isArray(generatedData?.cards) && generatedData.cards.length > 0
? generatedData.cards
: [
`🌟 ${tema}`,
`🤝 Vivência lúdica e cooperativa em sala`,
`✨ Cuidamos e aprendemos juntos todos os dias`
];
const finalRodape = generatedData?.rodape || rodape || (turmaContexto ? `${turmaContexto} • Professora Camila` : 'Educando com afeto, empatia e compromisso');
const finalPromptSugerido = generatedData?.promptImagem || promptImagem || `Cute children learning happily about ${tema}, 3D Pixar style, colorful, warm lighting, no text`;
// Montar HTML estilizado caso a interface use conteúdo rico
if (!conteudoHtml) {
conteudoHtml = `<h2>${titulo}</h2><p>Tema: ${tema}</p><p>Não foi possível gerar com IA. Edite o conteúdo livremente.</p>`;
const isLandscape = orientacao === 'paisagem';
const gridStyle = isLandscape
? 'display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; width: 100%;'
: 'display: flex; flex-direction: column; gap: 10px; width: 100%;';
const cardsHtml = finalCards.map(c => `
<div style="background: rgba(255, 255, 255, 0.45); backdrop-filter: blur(6px); border: 2px solid rgba(0, 0, 0, 0.08); border-radius: 12px; padding: 12px 16px; font-size: 1.05rem; font-weight: 600; display: flex; align-items: center; gap: 10px; box-shadow: 0 3px 8px rgba(0,0,0,0.04);">
<span>${c}</span>
</div>
`).join('');
conteudoHtml = `
${finalSubtitulo ? `<div style="text-align: center; font-size: 1.15rem; font-weight: 600; opacity: 0.9; margin-bottom: 12px; letter-spacing: 0.3px;">${finalSubtitulo}</div>` : ''}
<div style="${gridStyle}">${cardsHtml}</div>
${finalRodape ? `<div style="margin-top: auto; padding-top: 14px; text-align: center; font-size: 0.85rem; font-weight: 600; opacity: 0.75; border-top: 1px dashed rgba(0,0,0,0.15);">${finalRodape}</div>` : ''}
`.trim();
}
// Clean HTML from backticks
conteudoHtml = conteudoHtml.replace(/```html|```/g, '').trim();
// 2. Gerar Imagem Ilustrativa se solicitado
// 2. Gerar Imagem Ilustrativa com IA se solicitado
let generatedImageUrl = null;
if (promptImagem && promptImagem.trim().length > 3) {
const promptParaGerar = promptImagem?.trim() || (generatedData?.promptImagem ? generatedData.promptImagem : null);
if (promptParaGerar && promptParaGerar.length > 3) {
try {
console.log('[Cartazes] Refinando prompt de imagem para inglês (sem texto)...');
let refinedPrompt = promptImagem;
console.log('[Cartazes] Refinando prompt de imagem para estilo infantil e garantindo ZERO texto...');
let refinedPrompt = promptParaGerar;
try {
const refineRes = await callMinimax({
messages: [{
role: 'user',
content: `Translate the following Portuguese description into a clean English description suitable for an image generation prompt.
Make sure the prompt specifies a beautiful, colorful, kid-friendly vector/illustration style.
CRITICAL REQUIREMENT: Explicitly specify in the prompt that there should be absolutely no text, no words, no signs, no letters, no labels, no speech bubbles, and no writing of any kind.
Portuguese description: "${promptImagem}"
Return ONLY the English prompt, no extra chat.`
content: `Translate and refine this image request into an ultra-clean English prompt for an image generator.
Description: "${promptParaGerar}"
Subject context: Classroom poster for early childhood education (${tema}).
Art style: Friendly 3D Pixar character cartoon, vibrant soft colors, high resolution.
CRITICAL SAFETY INSTRUCTION: There must be ABSOLUTELY NO TEXT, no letters, no words, no signs, no speech bubbles, and no captions anywhere in the image.
Return ONLY the refined English prompt.`
}],
temperature: 0.3,
max_tokens: 250
temperature: 0.2,
max_tokens: 200
});
if (refineRes && refineRes.text) {
refinedPrompt = refineRes.text;
refinedPrompt = refineRes.text.replace(/["\n]/g, ' ').trim();
}
} catch (refineErr) {
console.error('[Cartazes] Erro ao refinar prompt:', refineErr);
console.error('[Cartazes] Erro ao refinar prompt:', refineErr.message);
}
console.log('[Cartazes] Gerando ilustração com prompt final:', refinedPrompt);
@@ -2271,7 +2362,7 @@ Return ONLY the English prompt, no extra chat.`
},
body: JSON.stringify({
model: 'image-01',
prompt: `${refinedPrompt}. Complete figures shown from top to bottom without cutoffs, kid-friendly illustration style, 3D Pixar, soft pastel colors, no text, no words, no letters, no writing, no labels.`,
prompt: `${refinedPrompt}, complete joyful composition, children book illustration, 3D Pixar, soft pastel lighting, clean background, no text, no words, no letters, no watermark.`,
n: 1
})
});
@@ -2293,9 +2384,12 @@ Return ONLY the English prompt, no extra chat.`
generatedImageUrl = `/generated-media/${fileName}`;
}
}
} else {
const errBody = await genResp.text();
console.warn('[Cartazes] Falha na API de imagem MiniMax:', genResp.status, errBody);
}
} catch (imgErr) {
console.error('[Cartazes] Erro na geração de imagem:', imgErr);
console.error('[Cartazes] Erro na geração de imagem:', imgErr.message);
}
}
@@ -2303,17 +2397,22 @@ Return ONLY the English prompt, no extra chat.`
const dbRes = await dbPool.query(
`INSERT INTO escola.cartazes (usuario_id, titulo, tema, cor_fundo, cor_texto, layout, conteudo, imagem_url)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at`,
[usuarioId, titulo, tema, corFundo || '#ffffff', corTexto || '#1e293b', layout, conteudoHtml, generatedImageUrl]
[usuarioId, finalTitulo, tema, corFundo || '#fff9eb', corTexto || '#2d3748', layout || 'educativo', conteudoHtml, generatedImageUrl]
);
res.json({
success: true,
id: dbRes.rows[0].id,
titulo,
titulo: finalTitulo,
subtitulo: finalSubtitulo,
badge: finalBadge,
cards: finalCards,
rodape: finalRodape,
promptImagem: finalPromptSugerido,
tema,
corFundo,
corTexto,
layout,
corFundo: corFundo || '#fff9eb',
corTexto: corTexto || '#2d3748',
layout: layout || 'educativo',
conteudo: conteudoHtml,
imagemUrl: generatedImageUrl,
createdAt: dbRes.rows[0].created_at
@@ -2325,6 +2424,44 @@ Return ONLY the English prompt, no extra chat.`
}
});
// Rota para upload direto de imagem/foto do professor para o cartaz
app.post('/api/cartazes/upload-image', requireAuth, async (req, res) => {
const { base64Data } = req.body;
if (!base64Data) {
return res.status(400).json({ error: 'Nenhuma imagem fornecida.' });
}
try {
const matches = base64Data.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
if (!matches || matches.length !== 3) {
return res.status(400).json({ error: 'Formato de imagem base64 inválido.' });
}
const imageBuffer = Buffer.from(matches[2], 'base64');
let fileExt = matches[1].split('/')[1] || 'png';
if (fileExt === 'jpeg') fileExt = 'jpg';
const mediaDir = path.join(__dirname, 'public', 'generated-media');
if (!fs.existsSync(mediaDir)) {
fs.mkdirSync(mediaDir, { recursive: true });
}
const fileName = `cartaz_upload_${Date.now()}_${Math.floor(Math.random() * 1000)}.${fileExt}`;
const filePath = path.join(mediaDir, fileName);
fs.writeFileSync(filePath, imageBuffer);
await backupMediaFile(filePath, imageBuffer);
res.json({
success: true,
imageUrl: `/generated-media/${fileName}`
});
} catch (err) {
console.error('[Cartazes Upload Image] Erro:', err);
res.status(500).json({ error: 'Falha ao salvar imagem enviada: ' + err.message });
}
});
app.post('/api/cartazes/decompose-scene', requireAuth, async (req, res) => {
const { tema, titulo, layers_manual } = req.body;
+4
View File
@@ -27,6 +27,10 @@ fi
COOLIFY_RESOURCE_UUID="zzmj37ya8lmntomh3bm8rivi"
COOLIFY_TOKEN="12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03"
echo -e "${YELLOW}🔄 Disparando Deploy na Coolify (camila.reifonas.cloud)...${NC}"
curl -s -X GET "https://painel.reifonas.cloud/api/v1/deploy?uuid=${COOLIFY_RESOURCE_UUID}&force=false" \
-H "Authorization: Bearer $COOLIFY_TOKEN"
echo -e "\n${GREEN}✅ Deploy engatilhado com sucesso!${NC}\n"
# 3. Sincronização pós-deploy do .env no container em background