diff --git a/public/app.js b/public/app.js index eefa56d..87a6648 100644 --- a/public/app.js +++ b/public/app.js @@ -7601,6 +7601,198 @@ document.addEventListener('DOMContentLoaded', () => { let dragStartWidth, dragStartHeight; let activeAssetTab = 'postit'; + // --- ESTÚDIO DE CENAS EM CAMADAS --- + const btnDecomposeScene = document.getElementById('btnDecomposeScene'); + const sceneDecomposePrompt = document.getElementById('sceneDecomposePrompt'); + const sceneDecomposeLoader = document.getElementById('sceneDecomposeLoader'); + const sceneDecomposeStatus = document.getElementById('sceneDecomposeStatus'); + const sceneLayersList = document.getElementById('sceneLayersList'); + const sceneLayersItems = document.getElementById('sceneLayersItems'); + + // Cria um item de camada de imagem direto no canvas (background ou sticker) + function addSceneLayerToCanvas(layerData) { + if (!canvasModeActive || !cartazPrintableArea) return; + + const item = document.createElement('div'); + item.className = 'canvas-item canvas-scene-layer'; + item.setAttribute('data-layer-name', layerData.name || ''); + item.setAttribute('data-rotation', '0'); + item.style.position = 'absolute'; + item.style.left = (layerData.left || 10) + '%'; + item.style.top = (layerData.top || 10) + '%'; + item.style.width = (layerData.width || 35) + '%'; + item.style.height = (layerData.height || 35) + '%'; + item.style.zIndex = layerData.isBackground ? '1' : String(10 + (layerData.zOffset || 0)); + item.style.background = 'transparent'; + + if (layerData.isBackground) { + // Fundo: cobrindo 100% e não selecionável como item de controle + item.style.left = '0'; + item.style.top = '0'; + item.style.width = '100%'; + item.style.height = '100%'; + item.style.zIndex = '0'; + item.style.pointerEvents = 'none'; // Por padrão, o fundo não captura eventos para facilitar a seleção dos elementos acima + item.innerHTML = ``; + item.title = '🖼️ Cenário de Fundo'; + } else { + item.innerHTML = ``; + item.title = '🎬 ' + (layerData.name || 'Elemento'); + } + + const handle = document.createElement('div'); + handle.className = 'canvas-resize-handle'; + item.appendChild(handle); + + cartazPrintableArea.appendChild(item); + + if (!layerData.isBackground) { + setupCanvasItemEvents(item); + // Permitir que o fundo seja selecionável após clicar nele diretamente + } else { + // para o fundo, adicionar capacidade de clicar para habilitar arrastar + item.style.pointerEvents = 'auto'; + setupCanvasItemEvents(item); + } + return item; + } + + // Renderiza a lista de camadas no painel lateral + function renderSceneLayers(layers) { + if (!sceneLayersItems) return; + sceneLayersItems.innerHTML = ''; + + const allLayers = [{ name: '🖼️ Cenário de Fundo', isBackground: true }, ...layers.map(l => ({ ...l }))]; + allLayers.forEach((layer, index) => { + const chip = document.createElement('div'); + chip.style.cssText = `display:flex; align-items:center; gap:8px; padding:5px 8px; + background:rgba(255,255,255,0.05); border:1px solid rgba(139,92,246,0.2); + border-radius:6px; font-size:0.78rem; color:var(--text-primary); cursor:pointer;`; + chip.innerHTML = ` + ${layer.isBackground ? '🌄' : '✨'} + ${layer.name} + Camada ${index} + `; + chip.title = `${layer.isBackground ? 'Fundo da cena' : 'Elemento: ' + layer.name}`; + sceneLayersItems.appendChild(chip); + }); + } + + if (btnDecomposeScene) { + btnDecomposeScene.addEventListener('click', async () => { + const prompt = sceneDecomposePrompt ? sceneDecomposePrompt.value.trim() : ''; + if (!prompt) { + showToast('Descreva a cena antes de gerar!', 'warning'); + return; + } + + // Garantir que o modo canvas está ativo + if (!canvasModeActive) { + if (cartazEditArea && cartazEditArea.style.display !== 'none') { + btnEditCartazToggle && btnEditCartazToggle.click(); + } + enterCanvasMode(); + } + + // Mostrar loading + btnDecomposeScene.disabled = true; + btnDecomposeScene.style.opacity = '0.6'; + if (sceneDecomposeLoader) { + sceneDecomposeLoader.style.display = 'flex'; + } + if (sceneLayersList) sceneLayersList.style.display = 'none'; + + const statusSteps = [ + 'Planejando a cena com IA...', + 'Identificando elementos e cenário...', + 'Gerando imagem de fundo...', + 'Gerando elementos individuais...', + 'Montando as camadas no canvas...' + ]; + let stepIdx = 0; + const statusInterval = setInterval(() => { + if (sceneDecomposeStatus && stepIdx < statusSteps.length) { + sceneDecomposeStatus.textContent = statusSteps[stepIdx++]; + } + }, 3500); + + try { + const titulo = activeCartazData?.titulo || ''; + const response = await fetch('/api/cartazes/decompose-scene', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('token')}` + }, + body: JSON.stringify({ tema: prompt, titulo }) + }); + + clearInterval(statusInterval); + + if (!response.ok) { + const errData = await response.json().catch(() => ({})); + throw new Error(errData.error || 'Erro na geração da cena.'); + } + + const data = await response.json(); + if (!data.success) throw new Error('Resposta inválida do servidor.'); + + if (sceneDecomposeStatus) sceneDecomposeStatus.textContent = 'Montando as camadas no canvas...'; + + // Limpar canvas antes de montar (remover outros items de cena se houver) + const oldLayers = cartazPrintableArea.querySelectorAll('.canvas-scene-layer'); + oldLayers.forEach(l => l.remove()); + + // Adicionar background + if (data.backgroundUrl) { + addSceneLayerToCanvas({ url: data.backgroundUrl, name: 'Cenário de Fundo', isBackground: true }); + } + + // Adicionar cada elemento com posição escalonada + if (Array.isArray(data.layers)) { + data.layers.forEach((layer, idx) => { + if (layer.url) { + const spread = [ + { left: 10, top: 30 }, + { left: 38, top: 35 }, + { left: 62, top: 28 }, + { left: 25, top: 55 } + ]; + const pos = spread[idx % spread.length]; + addSceneLayerToCanvas({ + url: layer.url, + name: layer.name || `Elemento ${idx + 1}`, + isBackground: false, + left: pos.left, + top: pos.top, + width: 28, + height: 28, + zOffset: idx + 1 + }); + } + }); + + // Renderizar lista de camadas no painel + renderSceneLayers(data.layers); + if (sceneLayersList) sceneLayersList.style.display = 'flex'; + } + + showToast('🎬 Cena montada em camadas! Clique nos elementos para mover, girar e redimensionar.', 'success'); + + } catch (err) { + clearInterval(statusInterval); + console.error('[Decompose Scene]', err); + showToast('Erro ao criar a cena em camadas: ' + err.message, 'error'); + } finally { + btnDecomposeScene.disabled = false; + btnDecomposeScene.style.opacity = '1'; + if (sceneDecomposeLoader) sceneDecomposeLoader.style.display = 'none'; + } + }); + } + function enterCanvasMode() { canvasModeActive = true; if (btnCanvasModeToggle) { diff --git a/public/index.html b/public/index.html index c3b6002..340afd6 100644 --- a/public/index.html +++ b/public/index.html @@ -1330,7 +1330,7 @@
-
+
@@ -1338,7 +1338,7 @@
-
+
@@ -1882,6 +1882,35 @@