N">
Dashboard — Bem-vindo à NEXUS Intelligence Suite

NEXUS Intelligence Suite 3.0 🚀

60 ferramentas de IA para copywriting, branding, tráfego pago, SEO, estratégia, UX, geração de prompts visuais, relatórios executivos e muito mais. Tudo em um só lugar.

🧠
60
Ferramentas Ativas
↑ 220% vs v1.0
0
Outputs Gerados
Sessão atual
10
Categorias
Copy · Brand · Ads · SEO · +6
Gerações / Mês
Cloudflare AI Workers
⚡ Acesso Rápido Ferramentas mais usadas
✍️

Copy Persuasivo

AIDA · PAS · Cialdini · Alta conversão

🎨

Prompts de Imagem IA

Midjourney · DALL-E · Flux · Ultra-realistas

🎬

Prompts de Vídeo IA

Sora · Runway · Kling · Cinematográfico

Studio IA — NOVO!

Gere imagens grátis + vídeos com Kling/Runway

📣

Meta Ads Completo

Criativos · Segmentação · Funil

📊

Relatório Executivo

Formal · Dados · Insights · Recomendações

🎯

Campanha 360°

Conceito · Canais · Cronograma · KPIs

🌟 Novidades v3.0 Agora disponível
🎬

Geração de Vídeo IA

Prompts para Sora, Runway, Kling, Pika

📈

Growth Hacking

10 experimentos · Loops virais · Escala

🚨

Gestão de Crises

Protocolo 24h · Templates · Recuperação

📋 Histórico da Sessão

🎨 Studio IA — Criação Visual Ultra-Realista

Gere imagens fotorrealistas de graça com Flux Schnell, SDXL e SDXL Lightning via Cloudflare AI. Crie vídeos cinematográficos com Kling, Runway Gen4 e WAN via fal.ai.

🤖 Modelo de Imagem
Estilo
Qualidade
Imagem gerada!
Imagem gerada pela NEXUS IA
🎬 Modelo de Vídeo
Duração
Proporção
Estilo
Enviando para fila de geração...
Vídeos levam entre 1-5 minutos dependendo do modelo
🖼️→🎬 Imagem para Vídeo
Duração
Proporção
Animando imagem...
Processo leva 1–3 minutos
✂️ Remover Fundo
⬆️ Upscale 4x
👁️ Análise Visual IA (Vision)
🔊 Síntese de Voz IA (TTS)
Voz
Gerando áudio...
Processo leva 30–60 segundos
🎵 Geração de Música IA
Duração
Compondo música...
Processo leva 30–90 segundos
🌐 Tradução Automática (Cloudflare AI — Grátis)
Idioma de origem
Traduzir para
`; const blob = new Blob([docHtml], {type:'application/msword;charset=utf-8'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `nexus-${toolId}-${Date.now()}.doc`; a.click(); URL.revokeObjectURL(url); showToast('📄 .doc exportado!', 'success'); } // ═══════════════════════════════════════════════════════ // TOAST // ═══════════════════════════════════════════════════════ function showToast(msg, type = 'success') { const t = document.getElementById('toast'); t.textContent = msg; t.className = `toast toast-${type} show`; setTimeout(() => t.classList.remove('show'), 3000); } // ═══════════════════════════════════════════════════════ // UTILS // ═══════════════════════════════════════════════════════ function escHtml(str) { return (str || '').replace(/&/g,'&').replace(//g,'>'); } // ═══════════════════════════════════════════════════════ // STUDIO IA — VARIÁVEIS DE ESTADO // ═══════════════════════════════════════════════════════ let selectedImgModel = 'flux'; let selectedVidModel = 'kling_v3'; let vidMode = 'short'; // 'short' or 'long' let currentImgSrc = null; let sessionGallery = []; let videoPollingTimer = null; let currentVideoReqId = null; let currentVideoModel = 'kling_v3'; let longVideoClips = []; // array de {index, request_id, status, video_url} let mergePollingTimer = null; // ═══════════════════════════════════════════════════════ // STUDIO IA — TABS // ═══════════════════════════════════════════════════════ function switchStudioTab(tab, el) { document.querySelectorAll('.studio-tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.studio-panel').forEach(p => p.classList.remove('active')); el.classList.add('active'); document.getElementById('studio-panel-' + tab).classList.add('active'); } // ═══════════════════════════════════════════════════════ // STUDIO IA — MODEL SELECTORS // ═══════════════════════════════════════════════════════ function selectImgModel(model, el) { selectedImgModel = model; document.querySelectorAll('#studio-panel-image .studio-model-btn').forEach(b => b.classList.remove('selected')); el.classList.add('selected'); } function selectVidModel(model, el) { selectedVidModel = model; currentVideoModel = model; document.querySelectorAll('#studio-panel-video .studio-model-btn').forEach(b => b.classList.remove('selected')); el.classList.add('selected'); // Atualizar info do vídeo longo se estiver nesse modo if (vidMode === 'long') updateLongVideoInfo(); } // ═══════════════════════════════════════════════════════ // STUDIO IA — MODO VÍDEO (curto/longo) // ═══════════════════════════════════════════════════════ function setVidMode(mode) { vidMode = mode; const shortOpts = document.getElementById('vid-short-options'); const longOpts = document.getElementById('vid-long-options'); const longAspect = document.getElementById('vid-long-aspect'); const btnShort = document.getElementById('btn-mode-short'); const btnLong = document.getElementById('btn-mode-long'); const genBtn = document.getElementById('btn-vid-gen'); const multiBox = document.getElementById('vid-multiclip-box'); if (mode === 'long') { shortOpts.style.display = 'none'; longOpts.style.display = 'block'; longAspect.style.display = 'block'; btnLong.classList.add('active'); btnShort.classList.remove('active'); genBtn.textContent = '🎞️ Gerar Vídeo Longo (múltiplos clips)'; genBtn.style.background = 'linear-gradient(135deg,#7c3aed,#059669)'; updateLongVideoInfo(); } else { shortOpts.style.display = 'block'; longOpts.style.display = 'none'; longAspect.style.display = 'none'; btnShort.classList.add('active'); btnLong.classList.remove('active'); genBtn.innerHTML = '🎬 Gerar Clip (5–15s)'; genBtn.style.background = 'linear-gradient(135deg,#7c3aed,#ec4899)'; if (multiBox) multiBox.style.display = 'none'; } } function updateLongVideoInfo() { const totalDur = parseInt(document.getElementById('vid-total-duration')?.value || 30); const clipDur = parseInt(document.getElementById('vid-clip-duration')?.value || 10); const maxClip = (selectedVidModel === 'runway') ? 10 : 15; const actualClip = Math.min(clipDur, maxClip); const numClips = Math.ceil(totalDur / actualClip); // Estimativa de custo const costPerSec = { kling_v3: 0.112, kling_v3_pro: 0.168, kling: 0.056, kling_pro: 0.084, wan: 0.05, runway: 0.08, }; const cps = costPerSec[selectedVidModel] || 0.112; const totalCost = (cps * totalDur).toFixed(2); const totalCostBRL = (cps * totalDur * 5.2).toFixed(2); const infoEl = document.getElementById('vid-long-info'); if (infoEl) { infoEl.innerHTML = ` 📊 ${numClips} clips de ${actualClip}s cada → Total: ~${totalDur}s
💰 Custo estimado: ~US$${totalCost} (~R$${totalCostBRL})
⏱️ Tempo estimado: ~${Math.ceil(numClips * 2)}-${numClips * 5} minutos (geração em paralelo) `; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — STYLE PRESETS // ═══════════════════════════════════════════════════════ const IMG_STYLE_PRESETS = { photorealistic: 'ultra photorealistic, 8K resolution, DSLR photography, natural lighting, hyper detailed, sharp focus, bokeh background', cinematic: 'cinematic shot, movie still, dramatic lighting, film grain, anamorphic lens, 2.39:1 aspect ratio, color graded, professional cinematography', product: 'product photography, clean white background, studio lighting, commercial photography, sharp details, professional advertising photo', portrait: 'editorial portrait photography, studio lighting, fashion magazine, high-end retouching, professional model', architecture: 'architectural photography, wide angle lens, golden hour lighting, 4K, professional real estate photography', abstract: 'abstract digital art, vibrant colors, dynamic composition, fluid forms, modern art, award winning', brand: 'brand identity design, premium luxury aesthetic, minimalist composition, high contrast, corporate photography', social: 'social media content, eye-catching composition, vibrant colors, perfect for Instagram, trending aesthetic', }; const VID_STYLE_PRESETS = { cinematic: 'cinematic camera movement, film grain, dramatic lighting, color graded, professional cinematography, sweeping shots', commercial: 'TV commercial quality, polished production, smooth camera movement, brand-focused, high production value', product: 'product showcase video, 360 rotation, hero lighting, clean background, premium advertising quality', nature: 'documentary style, natural lighting, smooth tracking shots, 4K nature footage quality', abstract: 'abstract motion graphics, vibrant colors, fluid animation, VJ loop style, mesmerizing movement', fashion: 'fashion film, editorial lighting, slow motion, high contrast, luxury aesthetics, runway quality', }; function applyImgStyle(style) { if (!style || !IMG_STYLE_PRESETS[style]) return; const current = document.getElementById('img-prompt').value.trim(); const preset = IMG_STYLE_PRESETS[style]; if (current && !current.includes(preset)) { document.getElementById('img-prompt').value = current + ', ' + preset; } else if (!current) { document.getElementById('img-prompt').placeholder = 'Ex: A stunning portrait of a CEO in a modern office... [adicione seu conceito e o preset será adicionado automaticamente]'; } } function applyVidStyle(style) { if (!style || !VID_STYLE_PRESETS[style]) return; const current = document.getElementById('vid-prompt').value.trim(); const preset = VID_STYLE_PRESETS[style]; if (current && !current.includes(preset)) { document.getElementById('vid-prompt').value = current + ', ' + preset; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — GENERATE IMAGE // ═══════════════════════════════════════════════════════ async function generateImage() { const prompt = document.getElementById('img-prompt').value.trim(); const negative = document.getElementById('img-negative')?.value?.trim() || ''; const enhance = document.getElementById('img-enhance')?.value || 'false'; const styleEl = document.getElementById('img-style'); const style = styleEl ? styleEl.value : ''; if (!prompt) { showToast('Digite um prompt para gerar a imagem!', 'error'); document.getElementById('img-prompt').focus(); return; } // Apply style preset to prompt if selected let finalPrompt = prompt; if (style && IMG_STYLE_PRESETS[style] && !prompt.includes(IMG_STYLE_PRESETS[style])) { finalPrompt = prompt + ', ' + IMG_STYLE_PRESETS[style]; } const btn = document.getElementById('btn-img-gen'); btn.disabled = true; btn.innerHTML = '
Gerando imagem...'; document.getElementById('img-result-box').classList.remove('show'); try { const res = await fetch(API_BASE + '/api/genimage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: finalPrompt, model: selectedImgModel, negative_prompt: negative, enhance, }), }); const data = await res.json(); if (!res.ok || data.error) { throw new Error(data.error || 'Erro ao gerar imagem'); } if (!data.image) throw new Error('Resposta inválida do servidor'); // Show result currentImgSrc = data.image; const imgEl = document.getElementById('studio-img'); imgEl.src = data.image; const box = document.getElementById('img-result-box'); box.classList.add('show'); const modelNames = { flux: 'Flux Schnell', sdxl: 'SDXL Base', lightning: 'SDXL Lightning' }; document.getElementById('img-result-title').textContent = `Gerada com ${modelNames[selectedImgModel] || selectedImgModel}`; document.getElementById('img-result-prompt').textContent = '📝 Prompt: ' + (data.prompt || finalPrompt); outputCount++; localStorage.setItem('nexus_outputs', outputCount); updateStats(); showToast('✅ Imagem gerada com sucesso!', 'success'); } catch (err) { showToast('Erro: ' + err.message, 'error'); console.error(err); } finally { btn.disabled = false; btn.innerHTML = '✨ Gerar Imagem Ultra-Realista'; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — DOWNLOAD / GALLERY // ═══════════════════════════════════════════════════════ function downloadCurrentImage() { if (!currentImgSrc) { showToast('Gere uma imagem primeiro!', 'error'); return; } const a = document.createElement('a'); a.href = currentImgSrc; a.download = 'nexus-studio-' + Date.now() + '.jpg'; a.click(); showToast('⬇️ Download iniciado!', 'success'); } function addToGallery() { if (!currentImgSrc) { showToast('Gere uma imagem primeiro!', 'error'); return; } sessionGallery.unshift({ src: currentImgSrc, prompt: document.getElementById('img-prompt').value }); renderGallery(); showToast('🗂️ Adicionado à galeria!', 'success'); } function renderGallery() { const gal = document.getElementById('studio-gallery'); const grid = document.getElementById('gallery-grid'); if (!sessionGallery.length) { gal.style.display = 'none'; return; } gal.style.display = 'block'; grid.innerHTML = sessionGallery.map((item, i) => ` `).join(''); } function removeFromGallery(i) { sessionGallery.splice(i, 1); renderGallery(); } function expandGalleryItem(i) { const item = sessionGallery[i]; currentImgSrc = item.src; document.getElementById('studio-img').src = item.src; document.getElementById('img-result-box').classList.add('show'); document.getElementById('img-result-prompt').textContent = '📝 ' + (item.prompt || ''); } // ═══════════════════════════════════════════════════════ // STUDIO IA — GENERATE VIDEO // ═══════════════════════════════════════════════════════ async function generateVideo() { // Redirecionar para modo longo se selecionado if (vidMode === 'long') { return generateLongVideo(); } const prompt = document.getElementById('vid-prompt').value.trim(); const duration = document.getElementById('vid-duration').value; const aspect_ratio = document.getElementById('vid-aspect').value; const vidStyle = document.getElementById('vid-style')?.value || ''; if (!prompt) { showToast('Digite um prompt para gerar o vídeo!', 'error'); document.getElementById('vid-prompt').focus(); return; } let finalPrompt = prompt; if (vidStyle && VID_STYLE_PRESETS[vidStyle] && !prompt.includes(VID_STYLE_PRESETS[vidStyle])) { finalPrompt = prompt + ', ' + VID_STYLE_PRESETS[vidStyle]; } const btn = document.getElementById('btn-vid-gen'); btn.disabled = true; btn.innerHTML = '⏳ Enviando para fila...'; // Hide setup box, show status box document.getElementById('vid-setup-box').style.display = 'none'; const statusBox = document.getElementById('vid-status-box'); statusBox.classList.add('show'); document.getElementById('vid-status-icon').textContent = '⏳'; document.getElementById('vid-status-text').textContent = 'Conectando ao serviço de IA...'; document.getElementById('vid-status-sub').textContent = 'Enviando prompt para ' + selectedVidModel.toUpperCase(); document.getElementById('vid-status-detail').textContent = ''; document.getElementById('vid-result').style.display = 'none'; try { const res = await fetch(API_BASE + '/api/genvideo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: finalPrompt, duration, aspect_ratio, model: selectedVidModel, }), }); const data = await res.json(); // Handle setup required (FAL_KEY not configured) if (data.setup_required || res.status === 503) { statusBox.classList.remove('show'); document.getElementById('vid-setup-box').style.display = 'block'; btn.disabled = false; btn.innerHTML = '🎬 Gerar Vídeo Cinematográfico'; return; } if (!res.ok || data.error) throw new Error(data.error || 'Erro ao iniciar geração'); currentVideoReqId = data.request_id; currentVideoModel = selectedVidModel; document.getElementById('vid-status-icon').textContent = '🎬'; document.getElementById('vid-status-text').textContent = 'Vídeo em processamento...'; document.getElementById('vid-status-sub').textContent = 'Estimativa: 1-5 minutos. Pode fechar e voltar depois.'; document.getElementById('vid-status-detail').textContent = 'ID: ' + data.request_id; // Start polling startVideoPolling(data.request_id, selectedVidModel); showToast('🎬 Vídeo enviado para geração!', 'success'); } catch (err) { statusBox.classList.remove('show'); showToast('Erro: ' + err.message, 'error'); console.error(err); btn.disabled = false; btn.innerHTML = '🎬 Gerar Vídeo Cinematográfico'; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — VIDEO POLLING // ═══════════════════════════════════════════════════════ function startVideoPolling(requestId, model) { if (videoPollingTimer) clearInterval(videoPollingTimer); let attempts = 0; const maxAttempts = 60; // 5 minutes (5s intervals) videoPollingTimer = setInterval(async () => { attempts++; if (attempts > maxAttempts) { clearInterval(videoPollingTimer); document.getElementById('vid-status-icon').textContent = '⏱️'; document.getElementById('vid-status-text').textContent = 'Tempo limite excedido'; document.getElementById('vid-status-sub').textContent = 'O vídeo pode ainda estar processando. Tente verificar mais tarde.'; document.getElementById('btn-vid-gen').disabled = false; document.getElementById('btn-vid-gen').innerHTML = '🎬 Gerar Vídeo Cinematográfico'; return; } try { const res = await fetch(API_BASE + '/api/pollvideo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: requestId, model }), }); const data = await res.json(); const statusText = document.getElementById('vid-status-detail'); const elapsed = Math.floor(attempts * 5); statusText.textContent = `Status: ${data.status || 'processando'} · ${elapsed}s · ID: ${requestId.substring(0,8)}...`; if (data.status === 'COMPLETED' && data.video_url) { clearInterval(videoPollingTimer); showVideoResult(data.video_url, requestId); } else if (data.status === 'FAILED') { clearInterval(videoPollingTimer); document.getElementById('vid-status-icon').textContent = '❌'; document.getElementById('vid-status-text').textContent = 'Falha na geração do vídeo'; document.getElementById('vid-status-sub').textContent = 'Tente novamente com um prompt diferente.'; document.getElementById('btn-vid-gen').disabled = false; document.getElementById('btn-vid-gen').innerHTML = '🎬 Gerar Vídeo Cinematográfico'; showToast('❌ Falha na geração do vídeo', 'error'); } } catch (e) { console.error('Polling error:', e); } }, 5000); } function showVideoResult(videoUrl, requestId) { document.getElementById('vid-status-icon').textContent = '✅'; document.getElementById('vid-status-text').textContent = 'Vídeo gerado com sucesso!'; document.getElementById('vid-status-sub').textContent = 'Seu vídeo está pronto para download e uso.'; document.getElementById('vid-progress').style.animation = 'none'; document.getElementById('vid-progress').style.width = '100%'; document.getElementById('vid-progress').style.background = 'var(--green)'; const resultEl = document.getElementById('vid-result'); resultEl.style.display = 'block'; resultEl.innerHTML = `
⬇️ Baixar MP4
`; document.getElementById('btn-vid-gen').disabled = false; document.getElementById('btn-vid-gen').innerHTML = '🎬 Gerar Vídeo Cinematográfico'; outputCount++; localStorage.setItem('nexus_outputs', outputCount); updateStats(); showToast('✅ Vídeo pronto!', 'success'); } // ═══════════════════════════════════════════════════════ // STUDIO IA — GERAÇÃO DE VÍDEO LONGO (múltiplos clips) // ═══════════════════════════════════════════════════════ async function generateLongVideo() { const prompt = document.getElementById('vid-prompt').value.trim(); if (!prompt) { showToast('Digite um prompt para gerar o vídeo!', 'error'); document.getElementById('vid-prompt').focus(); return; } const totalDuration = parseInt(document.getElementById('vid-total-duration')?.value || 30); const clipDuration = parseInt(document.getElementById('vid-clip-duration')?.value || 10); const aspectRatio = document.getElementById('vid-aspect-long')?.value || '16:9'; const btn = document.getElementById('btn-vid-gen'); btn.disabled = true; btn.innerHTML = '⏳ Enviando clips para fila...'; // Esconder status anterior, mostrar multiclip box document.getElementById('vid-setup-box').style.display = 'none'; document.getElementById('vid-status-box').classList.remove('show'); const multiBox = document.getElementById('vid-multiclip-box'); multiBox.style.display = 'block'; document.getElementById('vid-clips-list').innerHTML = '
⏳ Enviando para fila...
'; document.getElementById('vid-merge-btn-wrap').style.display = 'none'; try { const res = await fetch(API_BASE + '/api/genlongvideo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, total_duration: totalDuration, clip_duration: clipDuration, aspect_ratio: aspectRatio, model: selectedVidModel, }), }); const data = await res.json(); if (data.setup_required || res.status === 503) { multiBox.style.display = 'none'; document.getElementById('vid-setup-box').style.display = 'block'; btn.disabled = false; btn.innerHTML = '🎞️ Gerar Vídeo Longo'; return; } if (!res.ok || data.error) throw new Error(data.error || 'Erro ao iniciar geração'); // Guardar clips para polling longVideoClips = data.clips.map(c => ({ ...c, status: 'IN_QUEUE', video_url: null })); renderClipsList(); showToast(`🎞️ ${data.num_clips} clips enviados para geração!`, 'success'); btn.innerHTML = `⏳ Gerando ${data.num_clips} clips...`; // Iniciar polling dos clips startMultiClipPolling(); } catch (err) { multiBox.style.display = 'none'; showToast('Erro: ' + err.message, 'error'); btn.disabled = false; btn.innerHTML = '🎞️ Gerar Vídeo Longo'; } } function renderClipsList() { const listEl = document.getElementById('vid-clips-list'); listEl.innerHTML = longVideoClips.map((clip, i) => { const statusIcon = clip.status === 'COMPLETED' ? '✅' : clip.status === 'FAILED' ? '❌' : clip.status === 'IN_PROGRESS' ? '🔄' : '⏳'; const statusClass = clip.status === 'COMPLETED' ? 'completed' : clip.status === 'FAILED' ? 'failed' : clip.status === 'IN_PROGRESS' ? 'processing' : ''; const videoBtn = clip.video_url ? `▶ Ver` : ''; return `
${statusIcon} Clip ${i + 1} ${clip.scene_prompt ? clip.scene_prompt.substring(0, 50) + '...' : 'Cena ' + (i+1)} ${clip.status || 'Na fila'} ${videoBtn}
`; }).join(''); } function startMultiClipPolling() { if (mergePollingTimer) clearInterval(mergePollingTimer); let attempts = 0; const maxAttempts = 120; // 10 minutos mergePollingTimer = setInterval(async () => { attempts++; if (attempts > maxAttempts) { clearInterval(mergePollingTimer); showToast('⏱️ Tempo limite. Alguns clips podem ainda estar processando.', 'warning'); document.getElementById('btn-vid-gen').disabled = false; return; } const pendingClips = longVideoClips.filter(c => c.status !== 'COMPLETED' && c.status !== 'FAILED'); if (pendingClips.length === 0) { clearInterval(mergePollingTimer); return; } try { const res = await fetch(API_BASE + '/api/pollmulti', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clips: longVideoClips.map(c => ({ index: c.index, request_id: c.request_id })), model: selectedVidModel, }), }); const data = await res.json(); // Atualizar estado dos clips data.clips.forEach(updated => { const clip = longVideoClips.find(c => c.index === updated.index); if (clip) { clip.status = updated.status; if (updated.video_url) clip.video_url = updated.video_url; } }); renderClipsList(); const completed = longVideoClips.filter(c => c.status === 'COMPLETED').length; const total = longVideoClips.length; document.getElementById('btn-vid-gen').innerHTML = `⏳ ${completed}/${total} clips prontos...`; // Se todos completos, mostrar botão de merge if (data.all_completed && completed === total) { clearInterval(mergePollingTimer); document.getElementById('vid-merge-btn-wrap').style.display = 'block'; document.getElementById('btn-vid-gen').disabled = false; document.getElementById('btn-vid-gen').innerHTML = '🎞️ Gerar Vídeo Longo'; showToast(`✅ Todos os ${total} clips prontos! Clique em "Mesclar" para finalizar.`, 'success'); } } catch (e) { console.error('Polling multi-clip error:', e); } }, 5000); } async function mergeAllClips() { const videoUrls = longVideoClips .filter(c => c.status === 'COMPLETED' && c.video_url) .sort((a, b) => a.index - b.index) .map(c => c.video_url); if (videoUrls.length < 2) { showToast('Mínimo 2 clips necessários para mesclar!', 'error'); return; } const mergeBtn = document.querySelector('#vid-merge-btn-wrap button'); if (mergeBtn) { mergeBtn.disabled = true; mergeBtn.innerHTML = '⏳ Mesclando clips...'; } // Definir resolução baseado na proporção const aspectEl = document.getElementById('vid-aspect-long'); const aspect = aspectEl?.value || '16:9'; const resolution = aspect === '9:16' ? 'portrait_16_9' : aspect === '1:1' ? 'square_hd' : 'landscape_16_9'; try { const res = await fetch(API_BASE + '/api/mergeclips', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ video_urls: videoUrls, resolution }), }); const data = await res.json(); if (!res.ok || data.error) throw new Error(data.error || 'Erro no merge'); showToast('🔄 Mesclando clips... aguarde.', 'success'); if (mergeBtn) mergeBtn.innerHTML = '⏳ Processando merge...'; // Polling do merge startMergePolling(data.request_id); } catch (err) { showToast('Erro no merge: ' + err.message, 'error'); if (mergeBtn) { mergeBtn.disabled = false; mergeBtn.innerHTML = '⚡ Mesclar Todos os Clips → Vídeo Final'; } } } function startMergePolling(mergeRequestId) { let attempts = 0; const pollTimer = setInterval(async () => { attempts++; if (attempts > 60) { clearInterval(pollTimer); showToast('⏱️ Merge demorou demais. Tente novamente.', 'error'); return; } try { const res = await fetch(API_BASE + '/api/pollmerge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: mergeRequestId }), }); const data = await res.json(); if (data.status === 'COMPLETED' && data.video_url) { clearInterval(pollTimer); // Mostrar resultado final como um vídeo normal showFinalMergedVideo(data.video_url); } } catch (e) { console.error('Merge polling error:', e); } }, 5000); } function showFinalMergedVideo(videoUrl) { const multiBox = document.getElementById('vid-multiclip-box'); const resultArea = document.createElement('div'); resultArea.style.cssText = 'margin-top:16px;background:rgba(16,185,129,0.1);border:1px solid #10b981;border-radius:12px;padding:18px;text-align:center'; resultArea.innerHTML = `
🎬 Vídeo Final Mesclado — Pronto!
⬇️ Baixar Vídeo Final (MP4)
`; multiBox.appendChild(resultArea); outputCount++; localStorage.setItem('nexus_outputs', outputCount); updateStats(); showToast('🎬 Vídeo longo pronto! Duração completa.', 'success'); } // ═══════════════════════════════════════════════════════ // STUDIO IA — IMAGE TO VIDEO // ═══════════════════════════════════════════════════════ let selectedI2VModel = 'kling_v3'; let i2vPollingTimer = null; function selectI2VModel(model, el) { selectedI2VModel = model; document.querySelectorAll('#studio-panel-img2vid .studio-model-btn').forEach(b => b.classList.remove('selected')); el.classList.add('selected'); } async function generateImg2Vid() { const imageUrl = document.getElementById('i2v-image-url').value.trim(); const prompt = document.getElementById('i2v-prompt').value.trim(); const duration = document.getElementById('i2v-duration').value; const aspect = document.getElementById('i2v-aspect').value; if (!imageUrl) { showToast('Cole a URL da imagem!', 'error'); return; } const btn = document.getElementById('btn-i2v-gen'); btn.disabled = true; btn.innerHTML = '⏳ Enviando...'; const statusBox = document.getElementById('i2v-status-box'); statusBox.classList.add('show'); document.getElementById('i2v-status-icon').textContent = '⏳'; document.getElementById('i2v-status-text').textContent = 'Animando sua imagem...'; document.getElementById('i2v-status-sub').textContent = 'Enviando para ' + selectedI2VModel.toUpperCase(); document.getElementById('i2v-result').style.display = 'none'; try { const res = await fetch(API_BASE + '/api/img2vid', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ image_url: imageUrl, prompt, duration, aspect_ratio: aspect, model: selectedI2VModel }), }); const data = await res.json(); if (data.setup_required) { statusBox.classList.remove('show'); document.getElementById('vid-setup-box').style.display = 'block'; btn.disabled = false; btn.innerHTML = '🖼️→🎬 Animar Imagem'; return; } if (!res.ok || data.error) throw new Error(data.error || 'Erro'); document.getElementById('i2v-status-icon').textContent = '🎬'; document.getElementById('i2v-status-text').textContent = 'Animação em processamento...'; document.getElementById('i2v-status-sub').textContent = 'ID: ' + data.request_id?.substring(0,12) + '...'; showToast('🎬 Imagem enviada para animação!', 'success'); // Polling via pollfal if (i2vPollingTimer) clearInterval(i2vPollingTimer); let attempts = 0; i2vPollingTimer = setInterval(async () => { attempts++; if (attempts > 80) { clearInterval(i2vPollingTimer); btn.disabled = false; btn.innerHTML = '🖼️→🎬 Animar Imagem'; return; } try { const pr = await fetch(API_BASE + '/api/pollfal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: data.request_id, endpoint: data.model }), }); const pd = await pr.json(); document.getElementById('i2v-status-detail').textContent = `Status: ${pd.status} · ${attempts*5}s`; if (pd.status === 'COMPLETED' && pd.video_url) { clearInterval(i2vPollingTimer); const resEl = document.getElementById('i2v-result'); resEl.style.display = 'block'; resEl.innerHTML = `
⬇️ Baixar
`; document.getElementById('i2v-status-icon').textContent = '✅'; document.getElementById('i2v-status-text').textContent = 'Animação concluída!'; btn.disabled = false; btn.innerHTML = '🖼️→🎬 Animar Imagem'; showToast('✅ Animação pronta!', 'success'); } } catch(e) { console.error(e); } }, 5000); } catch(err) { statusBox.classList.remove('show'); showToast('Erro: ' + err.message, 'error'); btn.disabled = false; btn.innerHTML = '🖼️→🎬 Animar Imagem'; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — FERRAMENTAS DE IMAGEM // ═══════════════════════════════════════════════════════ async function removeBackground() { const url = document.getElementById('rmbg-url').value.trim(); if (!url) { showToast('Cole a URL da imagem!', 'error'); return; } const resultEl = document.getElementById('rmbg-result'); resultEl.style.display = 'block'; resultEl.innerHTML = '
⏳ Removendo fundo...
'; try { const res = await fetch(API_BASE + '/api/removebg', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ image_url: url }), }); const data = await res.json(); if (data.setup_required) { resultEl.innerHTML = '
⚠️ FAL_KEY necessária para esta função.
'; return; } if (data.error) throw new Error(data.error); if (data.image_url) { resultEl.innerHTML = `
⬇️ Baixar PNG`; showToast('✅ Fundo removido!', 'success'); } } catch(e) { resultEl.innerHTML = `
❌ ${e.message}
`; } } async function upscaleImage() { const url = document.getElementById('upscale-url').value.trim(); const scale = document.getElementById('upscale-factor').value; if (!url) { showToast('Cole a URL da imagem!', 'error'); return; } const statusEl = document.getElementById('upscale-status'); const resultEl = document.getElementById('upscale-result'); statusEl.style.display = 'block'; statusEl.textContent = '⏳ Enviando para upscale...'; resultEl.style.display = 'none'; try { const res = await fetch(API_BASE + '/api/upscaleimg', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ image_url: url, scale: parseInt(scale) }), }); const data = await res.json(); if (data.setup_required) { statusEl.textContent = '⚠️ FAL_KEY necessária.'; return; } if (data.error) throw new Error(data.error); statusEl.textContent = '🔄 Processando... (aguarde 30–60s)'; showToast('⏳ Upscale em andamento...', 'success'); // Poll let att = 0; const pollUp = setInterval(async () => { att++; if (att > 30) { clearInterval(pollUp); statusEl.textContent = '⏱️ Tempo limite.'; return; } const pr = await fetch(API_BASE + '/api/pollfal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: data.request_id, endpoint: 'fal-ai/clarity-upscale' }), }); const pd = await pr.json(); if (pd.status === 'COMPLETED' && pd.image_url) { clearInterval(pollUp); statusEl.style.display = 'none'; resultEl.style.display = 'block'; resultEl.innerHTML = `
⬇️ Baixar ${scale}x`; showToast(`✅ Upscale ${scale}x concluído!`, 'success'); } }, 5000); } catch(e) { statusEl.textContent = '❌ ' + e.message; } } async function analyzeImageVision() { const url = document.getElementById('vision-url').value.trim(); const q = document.getElementById('vision-question').value.trim() || 'Descreva esta imagem detalhadamente.'; if (!url) { showToast('Cole a URL da imagem!', 'error'); return; } const resultEl = document.getElementById('vision-result'); resultEl.style.display = 'block'; resultEl.innerHTML = '⏳ Analisando com Vision IA...'; try { const res = await fetch(API_BASE + '/api/analyzeimage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ image_url: url, question: q }), }); const data = await res.json(); if (data.error) throw new Error(data.error); resultEl.innerHTML = `Análise:
${data.analysis || 'Sem resposta'}`; showToast('✅ Análise concluída!', 'success'); } catch(e) { resultEl.innerHTML = '❌ ' + e.message; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — TTS // ═══════════════════════════════════════════════════════ let selectedTTSModel = 'playht'; function selectTTSModel(model, el) { selectedTTSModel = model; document.querySelectorAll('#studio-panel-tts .studio-model-btn').forEach(b => b.classList.remove('selected')); el.classList.add('selected'); } async function generateTTS() { const text = document.getElementById('tts-text').value.trim(); const voice = document.getElementById('tts-voice').value; if (!text) { showToast('Digite o texto para narrar!', 'error'); return; } const btn = document.getElementById('btn-tts-gen'); btn.disabled = true; btn.innerHTML = '⏳ Gerando áudio...'; const statusBox = document.getElementById('tts-status-box'); statusBox.classList.add('show'); document.getElementById('tts-status-icon').textContent = '⏳'; document.getElementById('tts-status-text').textContent = 'Sintetizando voz...'; document.getElementById('tts-result').style.display = 'none'; try { const res = await fetch(API_BASE + '/api/tts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, voice, model: selectedTTSModel }), }); const data = await res.json(); if (data.setup_required) { statusBox.classList.remove('show'); showToast('⚠️ FAL_KEY necessária para TTS', 'error'); btn.disabled = false; btn.innerHTML = '🔊 Gerar Narração em Áudio'; return; } if (!res.ok || data.error) throw new Error(data.error || 'Erro'); document.getElementById('tts-status-icon').textContent = '🔊'; document.getElementById('tts-status-text').textContent = 'Renderizando voz...'; showToast('🔊 Gerando narração...', 'success'); // Polling let att = 0; const pollTTS = setInterval(async () => { att++; if (att > 60) { clearInterval(pollTTS); btn.disabled = false; btn.innerHTML = '🔊 Gerar Narração em Áudio'; return; } const pr = await fetch(API_BASE + '/api/pollfal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: data.request_id, endpoint: data.model }), }); const pd = await pr.json(); if (pd.status === 'COMPLETED' && pd.audio_url) { clearInterval(pollTTS); const resEl = document.getElementById('tts-result'); resEl.style.display = 'block'; resEl.innerHTML = `
⬇️ Baixar MP3`; document.getElementById('tts-status-icon').textContent = '✅'; document.getElementById('tts-status-text').textContent = 'Narração pronta!'; btn.disabled = false; btn.innerHTML = '🔊 Gerar Narração em Áudio'; showToast('✅ Narração gerada!', 'success'); } }, 5000); } catch(err) { statusBox.classList.remove('show'); showToast('Erro: ' + err.message, 'error'); btn.disabled = false; btn.innerHTML = '🔊 Gerar Narração em Áudio'; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — MÚSICA IA // ═══════════════════════════════════════════════════════ let selectedMusicModel = 'stable-audio'; function selectMusicModel(model, el) { selectedMusicModel = model; document.querySelectorAll('#studio-panel-music .studio-model-btn').forEach(b => b.classList.remove('selected')); el.classList.add('selected'); } async function generateMusic() { const prompt = document.getElementById('music-prompt').value.trim(); const duration = document.getElementById('music-duration').value; if (!prompt) { showToast('Descreva a música!', 'error'); return; } const btn = document.getElementById('btn-music-gen'); btn.disabled = true; btn.innerHTML = '⏳ Compondo...'; const statusBox = document.getElementById('music-status-box'); statusBox.classList.add('show'); document.getElementById('music-status-icon').textContent = '🎵'; document.getElementById('music-status-text').textContent = 'Compondo música com IA...'; document.getElementById('music-result').style.display = 'none'; try { const res = await fetch(API_BASE + '/api/genmusic', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, duration: parseInt(duration), model: selectedMusicModel }), }); const data = await res.json(); if (data.setup_required) { statusBox.classList.remove('show'); showToast('⚠️ FAL_KEY necessária para Música IA', 'error'); btn.disabled = false; btn.innerHTML = '🎵 Compor Música com IA'; return; } if (!res.ok || data.error) throw new Error(data.error || 'Erro'); showToast('🎵 Composição em andamento...', 'success'); let att = 0; const pollMusic = setInterval(async () => { att++; if (att > 60) { clearInterval(pollMusic); btn.disabled = false; btn.innerHTML = '🎵 Compor Música com IA'; return; } const pr = await fetch(API_BASE + '/api/pollfal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: data.request_id, endpoint: data.model }), }); const pd = await pr.json(); if (pd.status === 'COMPLETED' && pd.audio_url) { clearInterval(pollMusic); const resEl = document.getElementById('music-result'); resEl.style.display = 'block'; resEl.innerHTML = `
⬇️ Baixar Música`; document.getElementById('music-status-icon').textContent = '✅'; document.getElementById('music-status-text').textContent = 'Música composta!'; btn.disabled = false; btn.innerHTML = '🎵 Compor Música com IA'; showToast('✅ Música pronta!', 'success'); } }, 5000); } catch(err) { statusBox.classList.remove('show'); showToast('Erro: ' + err.message, 'error'); btn.disabled = false; btn.innerHTML = '🎵 Compor Música com IA'; } } // ═══════════════════════════════════════════════════════ // STUDIO IA — TRADUÇÃO // ═══════════════════════════════════════════════════════ async function translateText() { const text = document.getElementById('translate-text').value.trim(); const from = document.getElementById('translate-from').value; const to = document.getElementById('translate-to').value; if (!text) { showToast('Cole o texto para traduzir!', 'error'); return; } if (from === to) { showToast('Idiomas de origem e destino são iguais!', 'error'); return; } const resultEl = document.getElementById('translate-result'); const outputEl = document.getElementById('translate-output'); resultEl.style.display = 'block'; outputEl.textContent = '⏳ Traduzindo...'; try { const res = await fetch(API_BASE + '/api/translate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, source_lang: from, target_lang: to }), }); const data = await res.json(); if (data.error) throw new Error(data.error); outputEl.textContent = data.translated_text || 'Sem resultado'; showToast('✅ Tradução concluída!', 'success'); } catch(e) { outputEl.textContent = '❌ ' + e.message; } } function copyTranslation() { const t = document.getElementById('translate-output').textContent; navigator.clipboard.writeText(t).then(() => showToast('📋 Tradução copiada!', 'success')); } // ═══════════════════════════════════════════════════════ // STUDIO IA — OPEN FROM TOOLS GRID // ═══════════════════════════════════════════════════════ // Override openTool to handle studio tools const _originalOpenTool = openTool; // We'll patch openTool to redirect studio tools to the studio page // ═══════════════════════════════════════════════════════ // SISTEMA DE FAVORITOS // ═══════════════════════════════════════════════════════ let favorites = JSON.parse(localStorage.getItem('nexus_favorites') || '[]'); function updateFavBadge() { const badge = document.getElementById('fav-count-badge'); if (!badge) return; if (favorites.length > 0) { badge.textContent = favorites.length; badge.style.display = 'inline-flex'; } else { badge.style.display = 'none'; } } function toggleResultFav(toolId) { const btn = document.getElementById('fav-result-btn'); const idx = favorites.indexOf(toolId); if (idx === -1) { favorites.push(toolId); if (btn) { btn.textContent = '★'; btn.classList.add('active'); } showToast('⭐ Adicionado aos favoritos!', 'success'); } else { favorites.splice(idx, 1); if (btn) { btn.textContent = '☆'; btn.classList.remove('active'); } showToast('Removido dos favoritos', 'info'); } localStorage.setItem('nexus_favorites', JSON.stringify(favorites)); updateFavBadge(); } function updateFavBtnState(toolId) { const btn = document.getElementById('fav-result-btn'); if (!btn) return; if (favorites.includes(toolId)) { btn.textContent = '★'; btn.classList.add('active'); } else { btn.textContent = '☆'; btn.classList.remove('active'); } } function showPage_favorites() { const grid = document.getElementById('fav-grid'); const subtitle = document.getElementById('fav-subtitle'); if (!grid) return; if (favorites.length === 0) { grid.innerHTML = ''; if (subtitle) subtitle.textContent = 'Nenhum favorito ainda. Use o ☆ nos resultados para salvar ferramentas.'; return; } if (subtitle) subtitle.textContent = `${favorites.length} ferramenta${favorites.length > 1 ? 's' : ''} favorita${favorites.length > 1 ? 's' : ''}`; const favTools = favorites.filter(id => TOOLS[id]); grid.innerHTML = favTools.map(id => { const t = TOOLS[id]; return `
${t.icon || '⚡'}
${t.name}
${t.desc || ''}
`; }).join(''); } function toggleFavCard(toolId, btn) { const idx = favorites.indexOf(toolId); if (idx !== -1) { favorites.splice(idx, 1); localStorage.setItem('nexus_favorites', JSON.stringify(favorites)); updateFavBadge(); showPage_favorites(); showToast('Removido dos favoritos', 'info'); } } // Hookar showPage para chamar showPage_favorites quando necessário const _origShowPage = typeof showPage === 'function' ? showPage : null; // Inicializar badge document.addEventListener('DOMContentLoaded', () => { updateFavBadge(); }); // Também inicializa imediatamente caso DOMContentLoaded já tenha disparado updateFavBadge();