{"id":572,"date":"2025-11-28T15:49:55","date_gmt":"2025-11-28T18:49:55","guid":{"rendered":"https:\/\/lp1d.com.br\/?page_id=572"},"modified":"2025-11-29T14:00:46","modified_gmt":"2025-11-29T17:00:46","slug":"tradutor","status":"publish","type":"page","link":"https:\/\/lp1d.com.br\/","title":{"rendered":"tradutor"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Gerador de Ata via Transcri\u00e7\u00e3o<\/title>\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n    <script src=\"https:\/\/unpkg.com\/lucide@latest\"><\/script>\n    <style>\n        @import url('https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@300;400;500;600&display=swap');\n        @import url('https:\/\/fonts.googleapis.com\/css2?family=Merriweather:wght@300;400;700&display=swap');\n\n        body { font-family: 'Inter', sans-serif; }\n        .scrollbar-hide::-webkit-scrollbar { display: none; }\n        .scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }\n        \n        \/* Cores din\u00e2micas para speakers *\/\n        .speaker-color-A { border-left-color: #ef4444; background-color: #fef2f2; }\n        .speaker-color-B { border-left-color: #3b82f6; background-color: #eff6ff; }\n        .speaker-color-C { border-left-color: #10b981; background-color: #ecfdf5; }\n        .speaker-color-D { border-left-color: #f59e0b; background-color: #fffbeb; }\n        .speaker-color-E { border-left-color: #8b5cf6; background-color: #f5f3ff; }\n        .speaker-tag-A { background-color: #fee2e2; color: #991b1b; }\n        .speaker-tag-B { background-color: #dbeafe; color: #1e40af; }\n        .speaker-tag-C { background-color: #d1fae5; color: #065f46; }\n        .speaker-tag-D { background-color: #fef3c7; color: #92400e; }\n        .speaker-tag-E { background-color: #ede9fe; color: #5b21b6; }\n\n        \/* Estilos do \"Papel\" da Ata *\/\n        .a4-paper {\n            background: white;\n            width: 210mm;\n            min-height: 297mm;\n            padding: 20mm;\n            margin: 0 auto;\n            box-shadow: 0 0 10px rgba(0,0,0,0.1);\n            font-family: 'Merriweather', serif;\n            color: #1f2937;\n            line-height: 1.6;\n            text-align: justify;\n            white-space: normal; \/* Garante que o texto flua *\/\n        }\n        @media print {\n            body * { visibility: hidden; }\n            #minutesScreen, #minutesScreen * { visibility: visible; }\n            #minutesScreen { position: absolute; left: 0; top: 0; width: 100%; background: white; z-index: 9999; }\n            .no-print { display: none !important; }\n            .a4-paper { box-shadow: none; margin: 0; width: 100%; }\n        }\n        \n        [contenteditable]:empty:before {\n            content: attr(placeholder);\n            color: #9ca3af;\n            cursor: text;\n        }\n        \n        \/* Indicador visual de edit\u00e1vel *\/\n        .transcript-text-edit:hover {\n            background-color: #f3f4f6;\n            cursor: text;\n        }\n        .transcript-text-edit:focus {\n            background-color: #ffffff;\n            box-shadow: 0 0 0 2px #e0e7ff;\n            outline: none;\n        }\n    <\/style>\n<\/head>\n<body class=\"bg-gray-100 min-h-screen\">\n\n    <!-- ================== TELA 1: UPLOAD ================== -->\n    <div id=\"uploadScreen\" class=\"min-h-screen flex items-center justify-center p-4 transition-opacity duration-300\">\n        <div class=\"bg-white w-full max-w-md p-8 rounded-2xl shadow-xl border border-gray-100\">\n            <div class=\"text-center mb-8\">\n                <div class=\"bg-indigo-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4\">\n                    <i data-lucide=\"mic\" class=\"text-indigo-600 w-8 h-8\"><\/i>\n                <\/div>\n                <h1 class=\"text-2xl font-bold text-gray-800\">Gerador de Ata<\/h1>\n                <p class=\"text-gray-500 text-sm mt-2\">Envie \u00e1udio para processar ou carregue um projeto salvo.<\/p>\n            <\/div>\n\n            <!-- Op\u00e7\u00e3o A: Upload de \u00c1udio -->\n            <form id=\"uploadForm\" class=\"space-y-4\">\n                <div id=\"dropZone\" class=\"relative border-2 border-dashed border-gray-300 rounded-xl p-6 text-center transition-all duration-200 cursor-pointer hover:border-indigo-400 group\">\n                    <input type=\"file\" id=\"audioFile\" name=\"audioFile\" accept=\"audio\/*,video\/*\" class=\"absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10\">\n                    <div class=\"space-y-3 pointer-events-none\">\n                        <i data-lucide=\"upload-cloud\" class=\"w-10 h-10 text-gray-400 mx-auto group-hover:text-indigo-500 transition-colors\"><\/i>\n                        <p class=\"text-sm text-gray-600 font-medium\">Novo Processamento<\/p>\n                        <p class=\"text-xs text-gray-400\">Arraste um \u00e1udio (MP3, WAV, M4A)<\/p>\n                    <\/div>\n                <\/div>\n\n                <div id=\"filePreview\" class=\"hidden bg-indigo-50 border border-indigo-100 rounded-lg p-3 flex items-center justify-between\">\n                    <div class=\"flex items-center space-x-3 overflow-hidden\">\n                        <i data-lucide=\"file-audio\" class=\"text-indigo-600 w-5 h-5 flex-shrink-0\"><\/i>\n                        <div class=\"truncate\">\n                            <p id=\"fileNameDisplay\" class=\"text-sm font-medium text-indigo-900 truncate\"><\/p>\n                            <p id=\"fileSizeDisplay\" class=\"text-xs text-indigo-500\"><\/p>\n                        <\/div>\n                    <\/div>\n                    <button type=\"button\" id=\"removeFileBtn\" class=\"text-gray-400 hover:text-red-500\"><i data-lucide=\"x\" class=\"w-4 h-4\"><\/i><\/button>\n                <\/div>\n\n                <button type=\"submit\" id=\"submitBtn\" disabled class=\"w-full bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 px-4 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed shadow-md flex justify-center items-center gap-2\">\n                    <span>Transcrever \u00c1udio<\/span>\n                    <i data-lucide=\"arrow-right\" class=\"w-4 h-4\"><\/i>\n                <\/button>\n            <\/form>\n\n            <div class=\"relative my-6\">\n                <div class=\"absolute inset-0 flex items-center\"><div class=\"w-full border-t border-gray-200\"><\/div><\/div>\n                <div class=\"relative flex justify-center text-sm\"><span class=\"px-2 bg-white text-gray-500\">Ou continuar trabalho<\/span><\/div>\n            <\/div>\n\n            <!-- Op\u00e7\u00e3o B: Upload de JSON -->\n            <div class=\"text-center\">\n                <input type=\"file\" id=\"jsonFile\" accept=\".json\" class=\"hidden\">\n                <button onclick=\"document.getElementById('jsonFile').click()\" class=\"w-full bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 font-semibold py-2 px-4 rounded-lg shadow-sm flex justify-center items-center gap-2 transition-colors\">\n                    <i data-lucide=\"folder-open\" class=\"w-4 h-4 text-yellow-600\"><\/i>\n                    <span>Carregar Arquivo (.json)<\/span>\n                <\/button>\n                <p class=\"text-xs text-gray-400 mt-2\">Carregue um projeto salvo OU um arquivo bruto da API.<\/p>\n            <\/div>\n\n            <div id=\"loadingState\" class=\"hidden mt-8 text-center\">\n                <div class=\"animate-spin rounded-full h-10 w-10 border-b-2 border-indigo-600 mx-auto mb-4\"><\/div>\n                <h3 class=\"text-gray-800 font-semibold\">Processando&#8230;<\/h3>\n                <p class=\"text-sm text-gray-500 mt-2\" id=\"loadingText\">Enviando \u00e1udio para transcri\u00e7\u00e3o.<\/p>\n            <\/div>\n            \n            <div id=\"errorMsg\" class=\"hidden mt-4 p-3 bg-red-50 text-red-700 text-sm rounded-lg text-center border border-red-200\"><\/div>\n        <\/div>\n    <\/div>\n\n    <!-- ================== TELA 2: EDITOR DE TRANSCRICAO ================== -->\n    <div id=\"editorScreen\" class=\"hidden min-h-screen flex flex-col md:flex-row bg-gray-50\">\n        \n        <!-- Sidebar: Identifica\u00e7\u00e3o de Oradores e Contexto -->\n        <div class=\"w-full md:w-96 bg-white border-r border-gray-200 p-6 flex-shrink-0 flex flex-col h-auto md:h-screen sticky top-0 z-20 shadow-sm overflow-y-auto\">\n            \n            <!-- Bot\u00e3o de Backup -->\n            <button id=\"downloadJsonBtn\" class=\"mb-6 w-full bg-pink-600 hover:bg-pink-700 text-white border border-pink-700 font-medium py-2 px-3 rounded-lg flex items-center justify-center gap-2 text-sm transition-colors shadow-sm\" title=\"Salvar trabalho para continuar depois\">\n                <i data-lucide=\"download\" class=\"w-4 h-4\"><\/i>\n                Baixar Backup (.json)\n            <\/button>\n\n            <!-- Bloco 1: Oradores -->\n            <div class=\"mb-6\">\n                <h2 class=\"text-lg font-bold text-gray-800 flex items-center gap-2 mb-2\">\n                    <i data-lucide=\"users\" class=\"w-5 h-5 text-indigo-600\"><\/i>\n                    Participantes\n                <\/h2>\n                <div id=\"speakersList\" class=\"space-y-3 pr-2\">\n                    <!-- Inputs de oradores -->\n                <\/div>\n            <\/div>\n\n            <hr class=\"border-gray-100 my-4\">\n\n            <!-- Bloco 2: Contexto da Ata -->\n            <div class=\"mb-6\">\n                <h2 class=\"text-lg font-bold text-gray-800 flex items-center gap-2 mb-3\">\n                    <i data-lucide=\"calendar\" class=\"w-5 h-5 text-indigo-600\"><\/i>\n                    Dados da Reuni\u00e3o\n                <\/h2>\n                <div class=\"space-y-3\">\n                    <div>\n                        <label class=\"text-xs font-semibold text-gray-500 uppercase\">Data<\/label>\n                        <input type=\"date\" id=\"meetingDate\" class=\"w-full text-sm border-gray-300 rounded focus:ring-indigo-500 px-2 py-1.5\">\n                    <\/div>\n                    <div class=\"grid grid-cols-2 gap-2\">\n                        <div>\n                            <label class=\"text-xs font-semibold text-gray-500 uppercase\">In\u00edcio<\/label>\n                            <input type=\"time\" id=\"meetingStart\" class=\"w-full text-sm border-gray-300 rounded focus:ring-indigo-500 px-2 py-1.5\">\n                        <\/div>\n                        <div>\n                            <label class=\"text-xs font-semibold text-gray-500 uppercase\">Fim<\/label>\n                            <input type=\"time\" id=\"meetingEnd\" class=\"w-full text-sm border-gray-300 rounded focus:ring-indigo-500 px-2 py-1.5\">\n                        <\/div>\n                    <\/div>\n                    <div>\n                        <label class=\"text-xs font-semibold text-gray-500 uppercase\">Local<\/label>\n                        <input type=\"text\" id=\"meetingLocation\" placeholder=\"Ex: Sala de Reuni\u00f5es\" class=\"w-full text-sm border-gray-300 rounded focus:ring-indigo-500 px-2 py-1.5\">\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <div class=\"mt-auto pt-4 border-t border-gray-100\">\n                <button id=\"generateAtaBtn\" class=\"w-full bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg shadow-sm flex justify-center items-center gap-2 transition-colors\">\n                    <i data-lucide=\"file-check\" class=\"w-4 h-4\"><\/i>\n                    <span>Gerar Ata de Reuni\u00e3o<\/span>\n                <\/button>\n            <\/div>\n        <\/div>\n\n        <!-- Main: Transcri\u00e7\u00e3o -->\n        <div class=\"flex-1 p-4 md:p-8 overflow-y-auto\">\n            <div class=\"max-w-4xl mx-auto\">\n                <div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4\">\n                    <h2 class=\"text-lg font-semibold text-gray-700\">Revis\u00e3o da Transcri\u00e7\u00e3o<\/h2>\n                    <div class=\"text-xs text-gray-500 bg-gray-100 px-3 py-1.5 rounded-full flex items-center gap-2\">\n                        <i data-lucide=\"edit-2\" class=\"w-3 h-3 text-indigo-500\"><\/i>\n                        <span>Clique no texto para editar<\/span>\n                    <\/div>\n                <\/div>\n\n                <div id=\"transcriptContainer\" class=\"space-y-4 pb-20\">\n                    <!-- Bal\u00f5es de fala -->\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <!-- ================== TELA 3: EDITOR DA ATA FINAL ================== -->\n    <div id=\"minutesScreen\" class=\"hidden min-h-screen bg-gray-200 pb-20\">\n        <div class=\"bg-white border-b border-gray-300 sticky top-0 z-30 px-6 py-4 shadow-sm no-print\">\n            <div class=\"max-w-6xl mx-auto flex flex-col md:flex-row justify-between items-center gap-4\">\n                <div class=\"flex items-center gap-3\">\n                    <button id=\"backToTranscriptionBtn\" class=\"text-gray-500 hover:text-gray-700 flex items-center gap-1 text-sm font-medium\">\n                        <i data-lucide=\"arrow-left\" class=\"w-4 h-4\"><\/i> Voltar\n                    <\/button>\n                    <div class=\"h-6 w-px bg-gray-300\"><\/div>\n                    <h2 class=\"text-xl font-bold text-gray-800 flex items-center gap-2\">\n                        <i data-lucide=\"file-text\" class=\"w-5 h-5 text-indigo-600\"><\/i>\n                        Ata da Reuni\u00e3o\n                    <\/h2>\n                <\/div>\n                \n                <div class=\"flex gap-2\">\n                    <button id=\"printBtn\" class=\"bg-white border border-gray-300 text-gray-700 hover:bg-gray-50 px-4 py-2 rounded-lg font-medium flex items-center gap-2\">\n                        <i data-lucide=\"printer\" class=\"w-4 h-4\"><\/i> Imprimir \/ PDF\n                    <\/button>\n                    <button id=\"copyBtn\" class=\"bg-indigo-600 text-white hover:bg-indigo-700 px-4 py-2 rounded-lg font-medium flex items-center gap-2\">\n                        <i data-lucide=\"copy\" class=\"w-4 h-4\"><\/i> Copiar\n                    <\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div class=\"py-8 overflow-y-auto h-full px-4\">\n            <div id=\"ataContent\" class=\"a4-paper outline-none\" contenteditable=\"true\" placeholder=\"A ata gerada aparecer\u00e1 aqui...\"><\/div>\n        <\/div>\n    <\/div>\n\n    <script>\n        lucide.createIcons();\n\n        \/\/ --- Configura\u00e7\u00f5es ---\n        const WEBHOOK_UPLOAD = 'https:\/\/automacao.re9treinamentos.com.br\/webhook\/audio-trancrever'; \n        const WEBHOOK_ATA = 'https:\/\/automacao.re9treinamentos.com.br\/webhook\/criar-ata';\n\n        \/\/ --- Vari\u00e1veis de Estado ---\n        let globalData = null; \n        let speakerMap = {}; \n        let currentFileName = \"\";\n\n        \/\/ --- Elementos DOM ---\n        const uploadScreen = document.getElementById('uploadScreen');\n        const editorScreen = document.getElementById('editorScreen');\n        const minutesScreen = document.getElementById('minutesScreen'); \n        const uploadForm = document.getElementById('uploadForm');\n        const dropZone = document.getElementById('dropZone');\n        const fileInput = document.getElementById('audioFile');\n        const loadingState = document.getElementById('loadingState');\n        const errorMsg = document.getElementById('errorMsg');\n        const ataContent = document.getElementById('ataContent');\n        const transcriptContainer = document.getElementById('transcriptContainer');\n\n        \/\/ --- FUN\u00c7\u00c3O DE CORRE\u00c7\u00c3O DE TEXTO ---\n        function fixEncoding(str) {\n            if (!str) return \"\";\n            let fixed = str;\n            try {\n                if (fixed.includes('%')) fixed = decodeURIComponent(fixed);\n                fixed = decodeURIComponent(escape(fixed));\n            } catch (e) {}\n\n            const replacements = {\n                '\u00c3\u00a1': '\u00e1', '\u00c3 ': '\u00e0', '\u00c3\u00a2': '\u00e2', '\u00c3\u00a3': '\u00e3', '\u00c3\u00a4': '\u00e3',\n                '\u00c3\u2030': '\u00c9', '\u00c3\u00a9': '\u00e9', '\u00c3\u00aa': '\u00ea', '\u00c3\u00ab': '\u00eb',\n                '\u00c3 ': '\u00cd', '\u00c3\u00ad': '\u00ed', '\u00c3\u00ae': '\u00ee', '\u00c3\u00af': '\u00ef',\n                '\u00c3\u201c': '\u00d3', '\u00c3\u00b3': '\u00f3', '\u00c3\u00b4': '\u00f4', '\u00c3\u00b5': '\u00f5', '\u00c3\u00b6': '\u00f6',\n                '\u00c3\u0161': '\u00da', '\u00c3\u00ba': '\u00fa', '\u00c3\u00bb': '\u00fb', '\u00c3\u00bc': '\u00fc',\n                '\u00c3\u2021': '\u00c7', '\u00c3\u00a7': '\u00e7', '\u00c3\u00b1': '\u00f1',\n                '\u00c3\u0192': '\u00c3', '\u00c3\u00b5e': '\u00f5e', '\u00c3\u00a3o': '\u00e3o',\n                '\u00c2': '', \n            };\n\n            for (const [key, value] of Object.entries(replacements)) {\n                fixed = fixed.split(key).join(value);\n            }\n            return fixed;\n        }\n\n        \/\/ --- FUN\u00c7\u00c3O AUXILIAR DE DOWNLOAD ---\n        function triggerDownload(data, filename) {\n            const dataStr = \"data:text\/json;charset=utf-8,\" + encodeURIComponent(JSON.stringify(data, null, 2));\n            const downloadAnchorNode = document.createElement('a');\n            downloadAnchorNode.setAttribute(\"href\", dataStr);\n            downloadAnchorNode.setAttribute(\"download\", filename);\n            document.body.appendChild(downloadAnchorNode);\n            downloadAnchorNode.click();\n            downloadAnchorNode.remove();\n        }\n\n        \/\/ --- FUN\u00c7\u00c3O DE SINCRONIZA\u00c7\u00c3O DE NOMES (Obrigatoriamente l\u00ea o DOM antes de salvar) ---\n        function syncSpeakersFromDOM() {\n            const inputs = document.querySelectorAll('.speaker-name-input');\n            inputs.forEach(input => {\n                const label = input.dataset.speaker;\n                if (label && input.value) {\n                    speakerMap[label] = input.value;\n                }\n            });\n        }\n\n        \/\/ --- HELPERS PARA ESTADO ATUAL ---\n        function collectCurrentState() {\n            const finalUtterances = [];\n            document.querySelectorAll('.transcript-row').forEach(row => {\n                const originalIndex = row.dataset.originalIndex;\n                const originalData = globalData.utterances[originalIndex];\n                \/\/ IMPORTANTE: Captura o texto que est\u00e1 na tela (editado pelo usu\u00e1rio)\n                const editedText = row.querySelector('.transcript-text-edit').innerText;\n                const speakerRef = row.querySelector('.speaker-display-name').dataset.speakerRef;\n\n                finalUtterances.push({\n                    ...originalData,\n                    text: editedText, \n                    speaker_name: speakerMap[speakerRef]\n                });\n            });\n            return finalUtterances;\n        }\n\n        \/\/ --- BOT\u00d5ES DE A\u00c7\u00c3O ---\n        \n        \/\/ 1. Exportar (Backup)\n        document.getElementById('downloadJsonBtn').addEventListener('click', () => {\n            \/\/ For\u00e7a sincroniza\u00e7\u00e3o dos nomes antes de gerar o JSON\n            syncSpeakersFromDOM();\n            const finalUtterances = collectCurrentState();\n            \n            const backupData = {\n                type: \"PROJECT_BACKUP\", \n                original_filename: currentFileName,\n                raw_global_data: globalData, \n                current_speaker_mapping: speakerMap, \n                edited_utterances: finalUtterances, \/\/ IMPORTANTE: Pega o texto editado\n                context: {\n                    date: document.getElementById('meetingDate').value,\n                    start: document.getElementById('meetingStart').value,\n                    end: document.getElementById('meetingEnd').value,\n                    location: document.getElementById('meetingLocation').value\n                },\n                timestamp: new Date().toISOString()\n            };\n\n            triggerDownload(backupData, \"backup_transcricao_\" + Date.now() + \".json\");\n        });\n\n        \/\/ 2. Importar (Carregar Projeto ou Raw API)\n        document.getElementById('jsonFile').addEventListener('change', (e) => {\n            const file = e.target.files[0];\n            if (!file) return;\n\n            const reader = new FileReader();\n            reader.onload = (event) => {\n                try {\n                    const loadedData = JSON.parse(event.target.result);\n                    \n                    if (Array.isArray(loadedData) || (loadedData.utterances && !loadedData.type)) {\n                        console.log(\"Detectado arquivo bruto da API.\");\n                        let transcriptData = Array.isArray(loadedData) ? loadedData[0] : loadedData;\n                        globalData = transcriptData;\n                        currentFileName = file.name;\n                        speakerMap = {};\n                        \/\/ FALSE: N\u00c3O baixa automaticamente arquivo carregado manualmente\n                        initializeEditor(globalData, false); \n                        alert(\"Arquivo carregado! Voc\u00ea pode editar o texto e os nomes.\");\n                    }\n                    else if (loadedData.raw_global_data) {\n                        console.log(\"Detectado backup de projeto.\");\n                        globalData = loadedData.raw_global_data;\n                        currentFileName = loadedData.original_filename || \"projeto_restaurado.json\";\n                        \n                        initializeEditor(globalData, false); \/\/ FALSE: N\u00e3o baixa backup de backup\n\n                        speakerMap = loadedData.current_speaker_mapping || {};\n                        for (const [key, value] of Object.entries(speakerMap)) {\n                            const input = document.querySelector(`.speaker-name-input[data-speaker=\"${key}\"]`);\n                            if (input) {\n                                input.value = value;\n                                updateTranscriptNames(key, value);\n                            }\n                        }\n\n                        \/\/ Restaura Texto Editado (L\u00ea do backup e joga no DOM na ordem correta)\n                        if (loadedData.edited_utterances) {\n                            \/\/ Pequeno delay para garantir que o DOM renderizou\n                            setTimeout(() => {\n                                const rows = document.querySelectorAll('.transcript-row');\n                                loadedData.edited_utterances.forEach((savedU, i) => {\n                                    if (rows[i]) {\n                                        const textDiv = rows[i].querySelector('.transcript-text-edit');\n                                        if (textDiv) textDiv.innerText = savedU.text;\n                                    }\n                                });\n                            }, 100);\n                        }\n\n                        if (loadedData.context) {\n                            document.getElementById('meetingDate').value = loadedData.context.date || \"\";\n                            document.getElementById('meetingStart').value = loadedData.context.start || \"\";\n                            document.getElementById('meetingEnd').value = loadedData.context.end || \"\";\n                            document.getElementById('meetingLocation').value = loadedData.context.location || \"\";\n                        }\n                    } else {\n                        throw new Error(\"Formato de JSON n\u00e3o reconhecido.\");\n                    }\n                    e.target.value = '';\n                } catch (err) {\n                    alert(\"Erro ao carregar arquivo: \" + err.message);\n                    console.error(err);\n                }\n            };\n            reader.readAsText(file);\n        });\n\n        \/\/ --- Drag & Drop \u00c1udio ---\n        dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('border-indigo-500', 'bg-indigo-50'); });\n        dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.classList.remove('border-indigo-500', 'bg-indigo-50'); });\n        dropZone.addEventListener('drop', (e) => {\n            e.preventDefault();\n            dropZone.classList.remove('border-indigo-500', 'bg-indigo-50');\n            if(e.dataTransfer.files.length) handleFile(e.dataTransfer.files[0]);\n        });\n        fileInput.addEventListener('change', (e) => { if(e.target.files.length) handleFile(e.target.files[0]); });\n\n        function handleFile(file) {\n            document.getElementById('fileNameDisplay').textContent = file.name;\n            document.getElementById('fileSizeDisplay').textContent = (file.size \/ (1024*1024)).toFixed(2) + ' MB';\n            dropZone.classList.add('hidden');\n            document.getElementById('filePreview').classList.remove('hidden');\n            document.getElementById('submitBtn').disabled = false;\n            currentFileName = file.name;\n        }\n\n        document.getElementById('removeFileBtn').addEventListener('click', () => {\n            fileInput.value = '';\n            dropZone.classList.remove('hidden');\n            document.getElementById('filePreview').classList.add('hidden');\n            document.getElementById('submitBtn').disabled = true;\n        });\n\n        uploadForm.addEventListener('submit', async (e) => {\n            e.preventDefault();\n            const file = fileInput.files[0];\n            if(!file) return;\n\n            document.getElementById('submitBtn').classList.add('hidden');\n            loadingState.classList.remove('hidden');\n            errorMsg.classList.add('hidden');\n            document.getElementById('loadingText').textContent = \"Enviando \u00e1udio para transcri\u00e7\u00e3o (pode demorar)...\";\n\n            const formData = new FormData();\n            const uniqueName = `audio_${Date.now()}_${Math.random().toString(36).substr(2, 5)}.${file.name.split('.').pop()}`;\n            formData.append('file', file, uniqueName);\n            formData.append('original_name', file.name);\n\n            try {\n                const response = await fetch(WEBHOOK_UPLOAD, { method: 'POST', body: formData });\n                if (!response.ok) throw new Error(`Erro ${response.status}: ${response.statusText}`);\n                const data = await response.json();\n                \n                let transcriptData = Array.isArray(data) ? data[0] : data;\n                if(!transcriptData.utterances) throw new Error(\"JSON inv\u00e1lido: campo 'utterances' n\u00e3o encontrado.\");\n\n                globalData = transcriptData;\n                initializeEditor(transcriptData, true); \/\/ TRUE: Download Autom\u00e1tico pois \u00e9 \u00e1udio novo\n\n            } catch (error) {\n                console.error(error);\n                loadingState.classList.add('hidden');\n                document.getElementById('submitBtn').classList.remove('hidden');\n                errorMsg.textContent = \"Erro ao processar: \" + error.message;\n                errorMsg.classList.remove('hidden');\n            }\n        });\n\n        function initializeEditor(data, autoDownload = false) {\n            uploadScreen.classList.add('hidden');\n            editorScreen.classList.remove('hidden');\n            \n            if (autoDownload) {\n                \/\/ Monta um objeto de backup b\u00e1sico para o download inicial\n                const initialBackup = {\n                    type: \"PROJECT_BACKUP\",\n                    original_filename: currentFileName,\n                    raw_global_data: data,\n                    utterances_source: data.utterances, \/\/ Salva o original\n                    timestamp: new Date().toISOString()\n                };\n                triggerDownload(initialBackup, \"transcricao_inicial_\" + Date.now() + \".json\");\n            }\n\n            const now = new Date();\n            document.getElementById('meetingDate').valueAsDate = now;\n            document.getElementById('meetingStart').value = now.toTimeString().substring(0,5);\n\n            const speakers = data.speakers_detected || [...new Set(data.utterances.map(u => u.speaker))];\n            \n            const speakersListEl = document.getElementById('speakersList');\n            speakersListEl.innerHTML = '';\n\n            speakers.sort().forEach(speakerLabel => {\n                speakerMap[speakerLabel] = `Participante ${speakerLabel}`; \n                const div = document.createElement('div');\n                div.className = \"bg-gray-50 p-2 rounded-lg border border-gray-200 text-sm\";\n                const colorClass = `speaker-tag-${speakerLabel}`;\n                div.innerHTML = `\n                    <div class=\"flex items-center gap-2 mb-1\">\n                        <span class=\"w-5 h-5 rounded flex items-center justify-center text-xs font-bold ${colorClass}\">${speakerLabel}<\/span>\n                        <span class=\"text-xs text-gray-400\">ID Orig.<\/span>\n                    <\/div>\n                    <input type=\"text\" value=\"Participante ${speakerLabel}\" data-speaker=\"${speakerLabel}\" class=\"speaker-name-input w-full text-sm border-gray-300 rounded px-2 py-1 shadow-sm h-8\" placeholder=\"Nome Real\">\n                `;\n                speakersListEl.appendChild(div);\n            });\n\n            document.querySelectorAll('.speaker-name-input').forEach(input => {\n                input.addEventListener('input', (e) => {\n                    const label = e.target.dataset.speaker;\n                    const newName = e.target.value;\n                    speakerMap[label] = newName;\n                    updateTranscriptNames(label, newName);\n                });\n            });\n\n            renderTranscript(data.utterances);\n            window.scrollTo(0, 0);\n        }\n\n        function renderTranscript(utterances) {\n            transcriptContainer.innerHTML = '';\n            \n            utterances.forEach((u, index) => {\n                const speakerLabel = u.speaker;\n                const startTime = formatTime(u.start);\n                const fixedText = fixEncoding(u.text);\n                \n                const row = document.createElement('div');\n                row.className = `transcript-row relative group flex flex-col border-l-4 pl-4 py-3 speaker-color-${speakerLabel} rounded-r-lg mb-4 hover:bg-gray-50 transition-colors pr-10`; \n                row.dataset.originalIndex = index;\n\n                row.innerHTML = `\n                    <div class=\"flex items-center justify-between mb-1\">\n                        <span class=\"font-bold text-gray-800 speaker-display-name\" data-speaker-ref=\"${speakerLabel}\">\n                            ${speakerMap[speakerLabel] || 'Participante ' + speakerLabel}\n                        <\/span>\n                        <span class=\"text-xs text-gray-400 font-mono\" title=\"Tempo em ms: ${u.start}\">${startTime}<\/span>\n                    <\/div>\n                    <div class=\"text-gray-700 leading-relaxed outline-none focus:bg-white focus:ring-2 focus:ring-indigo-100 p-1 rounded transcript-text-edit\" contenteditable=\"true\" title=\"Clique para editar este texto\">${fixedText}<\/div>\n                    <button class=\"delete-btn absolute top-2 right-2 text-gray-300 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1\" title=\"Excluir\"><i data-lucide=\"trash-2\" class=\"w-4 h-4\"><\/i><\/button>\n                `;\n\n                row.querySelector('.delete-btn').addEventListener('click', () => {\n                    if(confirm('Excluir esta fala?')) {\n                        row.remove();\n                    }\n                });\n\n                transcriptContainer.appendChild(row);\n            });\n            lucide.createIcons();\n        }\n\n        function updateTranscriptNames(label, newName) {\n            document.querySelectorAll(`.speaker-display-name[data-speaker-ref=\"${label}\"]`).forEach(el => {\n                el.textContent = newName || `Participante ${label}`;\n            });\n        }\n\n        function formatTime(ms) {\n            const totalSeconds = Math.floor(ms \/ 1000);\n            const minutes = Math.floor(totalSeconds \/ 60);\n            return `${minutes}:${(totalSeconds % 60).toString().padStart(2, '0')}`;\n        }\n\n        document.getElementById('generateAtaBtn').addEventListener('click', async () => {\n            const btn = document.getElementById('generateAtaBtn');\n            const date = document.getElementById('meetingDate').value;\n            const location = document.getElementById('meetingLocation').value;\n            if(!date || !location) { alert('Preencha Data e Local.'); return; }\n\n            const originalText = btn.innerHTML;\n            btn.disabled = true;\n            btn.innerHTML = `<div class=\"animate-spin rounded-full h-4 w-4 border-b-2 border-white\"><\/div> Gerando Ata...`;\n\n            syncSpeakersFromDOM();\n            const finalUtterances = collectCurrentState();\n\n            const contextData = {\n                date: document.getElementById('meetingDate').value,\n                start_time: document.getElementById('meetingStart').value,\n                end_time: document.getElementById('meetingEnd').value,\n                location: document.getElementById('meetingLocation').value\n            };\n\n            const payload = {\n                original_filename: currentFileName,\n                speaker_mapping: speakerMap,\n                final_transcript: finalUtterances,\n                context: contextData,\n                request_type: \"gerar_ata\",\n                created_at: new Date().toISOString()\n            };\n\n            try {\n                const response = await fetch(WEBHOOK_ATA, {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify(payload)\n                });\n\n                if(!response.ok) throw new Error(\"Erro na gera\u00e7\u00e3o da ata.\");\n\n                let content = \"\";\n                const contentType = response.headers.get(\"content-type\");\n                \n                if (contentType && contentType.includes(\"application\/json\")) {\n                    const jsonResp = await response.json();\n                    content = jsonResp.ata || jsonResp.text || jsonResp.content || jsonResp.message || JSON.stringify(jsonResp, null, 2);\n                } else {\n                    content = await response.text();\n                }\n\n                \/\/ --- CORRE\u00c7\u00c3O DE FORMATA\u00c7\u00c3O (ATA \"TIJOL\u00c3O\" FINAL) ---\n                \/\/ Remove qualquer tag HTML de bloco\n                content = content.replace(\/<\\\/?(p|div|br|ul|li|h[1-6])[^>]*>\/gi, \" \");\n                \/\/ Remove qualquer nova linha (\\n, \\r)\n                content = content.replace(\/(\\r\\n|\\n|\\r)\/gm, \" \");\n                \/\/ Limpa espa\u00e7os duplos\n                content = content.replace(\/\\s{2,}\/g, \" \");\n                \/\/ Formata\u00e7\u00e3o simples de negrito\n                content = content.replace(\/\\*\\*(.*?)\\*\\*\/g, '<strong>$1<\/strong>');\n\n                editorScreen.classList.add('hidden');\n                minutesScreen.classList.remove('hidden');\n                \n                ataContent.innerHTML = content;\n                window.scrollTo(0,0);\n\n            } catch (error) {\n                alert(\"Erro ao gerar ata: \" + error.message);\n            } finally {\n                btn.disabled = false;\n                btn.innerHTML = originalText;\n            }\n        });\n\n        document.getElementById('backToTranscriptionBtn').addEventListener('click', () => {\n            minutesScreen.classList.add('hidden');\n            editorScreen.classList.remove('hidden');\n        });\n\n        document.getElementById('printBtn').addEventListener('click', () => { window.print(); });\n        document.getElementById('copyBtn').addEventListener('click', () => {\n            const range = document.createRange();\n            range.selectNodeContents(ataContent);\n            const selection = window.getSelection();\n            selection.removeAllRanges();\n            selection.addRange(range);\n            document.execCommand('copy');\n            alert('Copiado!');\n        });\n    <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Gerador de Ata via Transcri\u00e7\u00e3o Gerador de Ata Envie \u00e1udio para processar ou carregue um projeto salvo. Novo Processamento Arraste um \u00e1udio (MP3, WAV, M4A) Transcrever \u00c1udio Ou continuar trabalho Carregar Arquivo (.json) Carregue um projeto salvo OU um arquivo bruto da API. Processando&#8230; Enviando \u00e1udio para transcri\u00e7\u00e3o. Baixar Backup (.json) Participantes Dados da Reuni\u00e3o [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-572","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/lp1d.com.br\/index.php?rest_route=\/wp\/v2\/pages\/572","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lp1d.com.br\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/lp1d.com.br\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/lp1d.com.br\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lp1d.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=572"}],"version-history":[{"count":19,"href":"https:\/\/lp1d.com.br\/index.php?rest_route=\/wp\/v2\/pages\/572\/revisions"}],"predecessor-version":[{"id":596,"href":"https:\/\/lp1d.com.br\/index.php?rest_route=\/wp\/v2\/pages\/572\/revisions\/596"}],"wp:attachment":[{"href":"https:\/\/lp1d.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=572"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}