# Validações Arquivadas: BATCH-094 a BATCH-110
Arquivo de registro histórico de validações e evidências gerado em 2026-08-18 como parte da polÃtica de otimização de contexto do SDD.
BATCH-094 - Tradução Completa dos Templates de Páginas do Módulo "publisher-index" para o Inglês (req-094)
-
[x] Tradução dos Arquivos HTML de Recursos:
-
[x]
publisher-index-adicionar.htmltotalmente traduzido do português para o inglês. -
[x]
publisher-index-editar.htmltotalmente traduzido do português para o inglês. -
[x]
publisher-index-clonar.htmltotalmente traduzido do português para o inglês.
-
-
[x] Validação:
-
[x] Estrutura HTML, IDs e classes CSS preservados de forma íntegra.
-
[x] Interface administrativa do módulo em inglês livre de termos em português ao acessar em
/en/.
-
Evidência de Validação (BATCH-094)
Evidência visual e automatizada reportada em 2026-07-21:
-
git diff --check→ Passou sem erros. -
Lint estático de HTML e JS → OK.
-
composer test(PHPUnit) → 110 testes passados (474 assertions). -
npm run test(Vitest) → 29 testes passados (todos aprovados). -
Nenhuma string acentuada remanescente em português nas páginas em inglês.
BATCH-095 - Tradução Completa da Editbar, Painéis e Overlays do Editor Visual para o Inglês (req-095)
-
[x] Tradução no Frontend (
html-editor.js):-
[x] Painel de IA (
#c2f-ai-panel) traduzido dinamicamente se o idioma for inglês. -
[x] Painel de Modelos (
#c2f-tpl-panel) traduzido dinamicamente se o idioma for inglês. -
[x] Floating Toolbar e Breadcrumb traduzidos dinamicamente se o idioma for inglês.
-
[x] Styler do Tailwind (
tailwindHelperConfig) traduzido dinamicamente se o idioma for inglês.
-
-
[x] Tradução na Toolbar e Backups (
dashboard.toolbar.js):-
[x] Painel de Adição (
#c2f-add-panel) traduzido dinamicamente se o idioma for inglês. -
[x] Painel de Backups (
#c2f-backup-panel) traduzido dinamicamente se o idioma for inglês. -
[x] Mensagens de alerta e erro traduzidas se o idioma for inglês.
-
-
[x] Tradução nos Controles Estáticos (
html-editor-visual-controls.js):-
[x] Painel de inclusão estático (
html-editor-add-panel) e lista de elementos HTML traduzidos se o idioma for inglês.
-
-
[x] Validação:
-
[x] Suíte Vitest (
npm run test) passa inteira sem erros. -
[x] Verificação visual do editor visual e da editbar no idioma inglês (exibindo toda a interface em inglês).
-
Evidência de Validação (BATCH-095)
Evidência visual e automatizada reportada em 2026-07-21:
-
git diff --check→ Passou sem erros. -
Lint estático de JS (
node --check) → Passou com sucesso. -
composer test(PHPUnit) → 110 testes passados (474 assertions). -
npm run test(Vitest) → 31 testes passados (incluindo 2 novos testes focados em internacionalização desenvolvidos para garantir a robustez da solução). -
Destaque para detecção dinâmica flexível de variantes do inglês (
en,en-us,en-gb) e herança cross-origin blindada para o iframe (window.parent.gestor.language).
BATCH-096 - Mapeamento Visual de Embeds, Proteção de Eventos, Suporte Híbrido a PDF e Modal Estruturado (req-096)
-
[x] Invólucro atômico e proteção de eventos (
html-editor.js):-
[x]
object,iframe,embed,video,audioe o contêiner.conn2flow-pdfjsenvolvidos em.conn2flow-embed-wrapper(badge de tipo + escudo.c2f-embed-shield+ 4 alças de redimensionamento). -
[x] Conteúdo embutido inerte a ponteiro (
.conn2flow-embed-inner > *), acabando com o event swallowing. -
[x]
resolveEditabledevolve o invólucro para escudo/alça/embed (bloco atômico) egetEditTypedevolveembed. -
[x]
wrapEmbedsidempotente; ignora UI do editor, iframes de sistema do Live Editor e embeds dentro de blocos dinâmicos (widget/.c2f-dyn-box). -
[x] Duplo clique no escudo abre o modal já na aba correspondente ao tipo.
-
[x] Redimensionamento por alça grava
style.width/heighte remove os atributos legadoswidth/height.
-
-
[x] Reversão limpa na persistência:
-
[x]
extractUserHtmlremove o invólucro SEMPRE (save e snapshots de undo) — o banco recebe apenas a tag limpa. -
[x]
applyState(undo/redo) reconstrói os invólucros a partir da tag limpa.
-
-
[x] Modal
#c2f-he-embed-modalem 4 abas:-
[x] Aba 1: fonte (
src/data) + seletor de arquivo do servidor (aceita PDF/vídeo/documento), largura/altura com unidade (px/%/vh) etitle. -
[x] Aba 2: 3 motores de PDF com parâmetros do PDF.js (zoom, barra de ferramentas, página inicial, modo de rolagem) e aviso quando a fonte não é PDF.
-
[x] Aba 3:
allowfullscreen,sandbox,controls,autoplay,loop,muted,poster. -
[x] Aba 4: gerenciador dinâmico de
<param>, HTML de fallback customizado, estilos inline e classes extras. -
[x] Detecção automática de PDF ao selecionar arquivo
.pdf(abre a aba de motores) ou ao digitar/colar um link.pdf.
-
-
[x] 3 motores de exibição de PDF:
-
[x] A —
<object type="application/pdf">+ fallback amigável (mensagem + botão estilizado, com estilos inline para funcionar no site publicado). -
[x] B —
<div class="conn2flow-pdfjs" data-pdf-*>renderizado em canvas pelo runtime novogestor/assets/interface/pdf-viewer.js. -
[x] C —
<iframe src="[REDACTED]>com URL absolutizada e codificada.
-
-
[x] Injeção de assets do PDF.js:
-
[x] Core:
gestor_pdf_viewer_detectar()/gestor_pdf_viewer_assets()(bibliotecagestor) +gestor_pagina_pdf_viewer()no pipeline (gestor/gestor.php), após os widgets e antes da serialização de JS — inclui os assets só nas páginas que usam o leitor. -
[x] Editor clássico:
montarPdfViewerHead()injeta o runtime nosrcdocdo preview e do editor visual (página e layout).
-
-
[ ] Homologação runtime (deploy
Update => Core): pendente com o operador.
Evidência de Validação (BATCH-096)
Reportada pelo executor em 2026-07-29:
-
php -l→gestor/gestor.php,gestor/bibliotecas/gestor.php,gestor/bibliotecas/html-editor.php,tests/Unit/PHP/PdfViewerAssetsTest.php→ 4/4 OK. -
node --check→html-editor.js,html-editor-interface.js,pdf-viewer.js,dashboard.toolbar.js→ 4/4 OK.dashboard.jsonválido. -
npx vitest run→ 50/50 (8 arquivos), incluindo o novotests/Unit/JS/html-editor-embed.test.js18/18: envelopamento das 5 tags + PDF.js, atomicidade (escudo/alça/embed → invólucro), idempotência, exclusão de UI/sistema/blocos dinâmicos, reversão nogetCleanHtml, round-trip de snapshot/applyState, detecção de tipo, geração dos 3 motores, roteamento dobuildEmbedMarkup, modal (4 abas, campos populados, troca de motor preservando fonte/dimensões, aviso de não-PDF),applyEmbedSize, layout do invólucro (block com largura fluida × fit-content com largura fixa) e tradução para inglês. -
composer test(PHPUnit) → 158/158 (660 assertions, 4 skipped) com o novoPdfViewerAssetsTest5/5. A única deprecation reportada é pré-existente (TwoFactorTest, metadados em doc-comment). -
Cache-bust:
biblioteca-html-editor1.4.12→1.5.0,dashboard.json1.0.11→1.0.12, motor no Live Editor?v=c2f10→?v=c2f11.
Pendências
-
Runtime (após deploy
Update => Core):-
Inserir um PDF local em
/admin-paginas/(Editor Visual) e no Live Editor; alternar os 3 motores e conferir a renderização. -
Conferir em desktop e em simulação mobile (Android/iOS) a exibição inline (motores B/C) e o fallback amigável (motor A).
-
Conferir que o HTML salvo no banco contém apenas a tag limpa (sem
conn2flow-embed-wrapper/c2f-embed-shield).
-
-
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-097 - Correções de Homologação do BATCH-096, Edição Avançada Separada e Embeds no Painel "+" (req-097)
-
[x] Fix 1 — duplicação/vazamento de embeds no save/reload e na Editbar:
-
[x] Causa-raiz confirmada pela evidência (temp/html-output.html): a 2ª cópia do
<object>estava FORA do#c2f-page-content, como filho de#c2f-layout-root— ou seja, salva emlayouts.html. Como o layout é compartilhado por todas as páginas e pela página da Editbar (renderizada no iframe da barra), o embed aparecia 2–3 vezes e "vazava" para dentro da barra. -
[x]
insertionRoot(): inserção sem alvo (código customizado e painel "+") vai para#c2f-page-contentquando ele existe; o fallback do modo de inserção deixou de usardocument.body. -
[x]
isEditorOwnedreconhece#c2f-site-toolbar,#c2f-device-preview(e descendentes) e#c2f-save-loader— blindawrapEmbeds,resolveEditable,findEditableFromPointe a extração de uma só vez. -
[x]
extractUserHtmlremove os elementos de sistema do clone, limpa o leitor PDF.js renderizado (canvas/toolbar +data-c2f-pdfjs-ready) e apaga resíduos órfãos de invólucro persistidos por versão anterior (extração idempotente).
-
-
[x] Fix 2 — interatividade do embed no site publicado:
-
[x]
disable()desembrulha os embeds no DOM vivo (sem escudo e sempointer-events:none) eenable()reconstrói — o escudo passa a existir só enquanto o editor está habilitado (vale para sair da edição, preview de dispositivo e salvamento). -
[x] O markup persistido nasce com
position:relative;z-index:1(estilos extras do usuário têm precedência), para o embed não ficar sob camadas decorativas absolutas do template (absolute inset-0 z-0), que capturavam o ponteiro.
-
-
[x] Fix 3 — z-index do seletor de arquivos:
#c2f-he-embed-modalem1000000e overlay do gerenciador (#c2f-he-imagepick-overlay) em1000060. -
[x] Fix 4 — preview ao vivo do Motor B:
ensurePdfViewer()carregainterface/pdf-viewer.jssob demanda (o runtime busca a lib do PDF.js) erefreshPdfJsViewers()dispara a renderização ao aplicar o modal, ao envolver embeds e noapplyState. O runtime deixou de escreverdisplay/flex-directioninline no contêiner (foi para CSS), preservando ostyleque será salvo. -
[x] Item 5 — dropdown "Página": "Editar Avançado" sempre em
/admin-paginas/editar/?id=…; novo "Editar Publicação Avançado" (/publisher-pages/editar/?id=…&publisher_id=…) exibido só quando há publicação vinculada (bloco<!-- dropdown-page-publisher -->removido pormodelo_tag_del), nos templates pt-br e en. -
[x] Item 6 — painel "+":
object,iframe,embed,videoeaudionas listas de elementos do Live Editor e do editor clássico;buildElementcria a estrutura limpa e a inserção abre o modal de embed automaticamente. -
[ ] Homologação runtime (deploy
Update => Core): pendente com o operador.
Evidência de Validação (BATCH-097)
Reportada pelo executor em 2026-07-30:
-
php -l→gestor/modulos/dashboard/dashboard.php,gestor/bibliotecas/html-editor.php→ OK. -
node --check→html-editor.js,dashboard.toolbar.js,pdf-viewer.js,html-editor-visual-controls.js→ 4/4 OK.dashboard.jsonválido. -
npx vitest run→ 58/58 (8 arquivos);tests/Unit/JS/html-editor-embed.test.jspassou de 19 para 27 casos, com 8 novos cobrindo: destino da inserção (conteúdo × layout), isolamento do iframe da Editbar/preview de dispositivo (wrap,resolveEditablee extração), limpeza do leitor PDF.js renderizado, limpeza de resíduos de invólucro,disable()/enable()do escudo,position:relative;z-index:1com precedência dos estilos do usuário, z-index picker > modal e os 5 tipos de embed do painel "+". -
composer test(PHPUnit) → 158/158 (660 assertions, 4 skipped) sem regressão. -
Cache-bust:
biblioteca-html-editor1.5.0→1.5.1,dashboard.json1.0.12→1.0.13, motor no Live Editor?v=c2f11→?v=c2f12.
Pendências
-
Runtime (após deploy
Update => Core):-
Inserir
<object>pelo painel "+" e por código customizado, salvar e recarregar: confirmar UMA ocorrência, dentro do conteúdo, sem cópia no layout nem dentro da Editbar. -
Sair do modo de edição e conferir que o PDF rola/amplia normalmente; repetir na página publicada como visitante.
-
Abrir o modal de embed → seletor de arquivos deve ficar À FRENTE; escolher um
.pdfe confirmar a abertura automática da aba de motores. -
Selecionar o Motor B e conferir o canvas renderizando ao vivo no editor; salvar e conferir no banco que o contêiner foi gravado vazio (só com os
data-pdf-*). -
Dropdown "Página": "Editar Avançado" →
/admin-paginas/; em página de publicação, "Editar Publicação Avançado" →/publisher-pages/compublisher_id.
-
-
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-098 - Área de Transferência Persistente do Editor Visual (demanda direta do Chefe, 2026-07-30)
-
[x] Persistência da cópia:
-
[x]
copySelected()grava o bloco emlocalStorage['c2f-he-clipboard']({html, ts, origem}) e SUBSTITUI a cópia anterior. -
[x]
initClipboard()recupera a cópia no boot do editor — o botãohe-tb-pastejá nasce visível numa página nova. -
[x] Listener de
storage: cópia feita em outra aba atualiza o estado do botão. -
[x]
localStorageindisponível ou cota estourada não quebra o editor (fallback silencioso para memória).
-
-
[x] Sanitização do bloco copiado (
buildClipboardMarkup): remove invólucro de embed e leitor PDF.js renderizado (UI de runtime), removedata-c2f-variable/contenteditable(dependem dovarMapda página de origem) e preservadata-c2f-marker(widget colado continua widget). -
[x] Colagem segura:
-
[x]
remapPastedIds()renumeradata-c2f-widget-ide reregistra wrappers clássicos nowidgetsMap— colar na mesma página do original não faz oreconstructOriginaldescartar um dos grupos no save. -
[x] Colar SEM seleção insere no fim do conteúdo editável (
insertionRoot()), nunca na raiz do layout. -
[x] Ctrl+V passa a funcionar sem seleção (usa
hasClipboard()).
-
-
[ ] Homologação runtime (deploy
Update => Core): pendente com o operador.
Evidência de Validação (BATCH-098)
Reportada pelo executor em 2026-07-30:
-
node --check gestor/assets/interface/html-editor.js→ OK. -
npx vitest run→ 64/64, com 6 casos novos emtests/Unit/JS/html-editor.live.test.js: gravação saneada nolocalStorage, recuperação por uma instância nova (outra página), substituição ao copiar de novo, colagem sem seleção indo para o conteúdo, renumeração de ids de widget e visibilidade do botão "Colar". -
composer test(PHPUnit) → 158/158 sem regressão. -
Cache-bust:
biblioteca-html-editor1.5.1→1.5.2, motor no Live Editor?v=c2f12→?v=c2f13.
Pendências
-
Runtime (após deploy
Update => Core): copiar uma seção numa página, sair da edição, abrir outra página, entrar em edição e colar (com e sem elemento selecionado); conferir que copiar de novo substitui a cópia e que o bloco colado é salvo corretamente. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-099 - Upload e Gestão de Pastas Liberados no Modo Picker do Admin-Arquivos (demanda direta do Chefe, 2026-07-30)
-
[x] Ferramentas visíveis no picker: removida a ocultação do bloco
<!-- normal-tools -->(botões "Adicionar" e "Nova pasta" + checkbox "Selecionar todos") quandopaginaIframeestá ativo. -
[x] Retorno da tela de envio: o botão voltar deixou de ser removido no picker — sem ele o usuário ficaria preso na tela de upload dentro do iframe. O destino já preservava o modo (
admin-arquivos/?paginaIframe=sim). -
[x] Impacto verificado antes da remoção:
-
[x] O href do botão "Adicionar" já recebia
?paginaIframe=simno PHP (#paginaIframe#) e&paginaIframe=simno JS (atualizarAddHref()), com a pasta corrente — ou seja, o fluxo de upload no iframe já estava implementado e apenas inacessível. -
[x] "Nova pasta", renomear, excluir, seleção em lote e a barra de seleção não são condicionados ao picker no JS — passam a funcionar sem alteração.
-
[x] O upload é assíncrono e não redireciona, então o modo picker não se perde durante o envio.
-
[x] Diferenciações legítimas do picker preservadas: célula de arquivo com botão "Selecionar" (em vez de "Copiar URL"), galeria com a mesma regra e ausência do cache de última pasta (
localStorage).
-
-
[ ] Homologação runtime (deploy
Update => Core): pendente com o operador.
Evidência de Validação (BATCH-099)
Reportada pelo executor em 2026-07-30:
-
php -l gestor/modulos/admin-arquivos/admin-arquivos.php→ OK. -
composer test(PHPUnit) → 158/158 (660 assertions, 4 skipped) sem regressão. -
npx vitest run→ 64/64 sem regressão. -
Sem cobertura automatizada específica: a mudança é de renderização condicional de template (exige banco + componentes), então a verificação é manual/runtime — registrado aqui conforme a regra final do checklist.
Pendências
-
Runtime (após deploy
Update => Core): abrir o seletor de arquivos por um campo de imagem e pelo modal de embed; enviar um arquivo novo (PDF e imagem) direto no iframe, criar uma pasta, voltar pela seta e selecionar o arquivo recém-enviado. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-100 - Mídia Embutida: 403 em Arquivos com Espaço, Streaming (HTTP Range) e Dimensionamento do Áudio (2026-07-30)
-
[x] Diagnóstico do 403 (medido, não inferido):
-
[x]
curlno container isolou a variável:mp4/oggSEM espaço → 404 normal do gestor; qualquer extensão COM%20→ 403 do Apache. -
[x]
localhost-error.log:AH10411: Rewritten query string contains control characters or spaces— o.htaccessinjeta o caminho decodificado na query (index.php?_gestor-caminho=$1) e o Apache ≥2.4.53 recusa espaços. -
[x] Confirmado que codificar a URL no picker NÃO resolveria: o navegador já enviava
%20(visível no console) e o 403 ocorria mesmo assim — a decodificação acontece dentro do mod_rewrite. -
[x]
.htaccessdo instalador (gestor-instalador/public-access/.htaccess) já traz[B,L,QSA], assim como sites locais criados recentemente → novas instalações não têm o defeito; o mirror local antigo foi alinhado (arquivo emdev-environment/data/, git-ignored). -
[x]
Installer.php(§713-755) apenas ajusta oRewriteBasee, sem SSL, remove a regra de redirect HTTPS (regex ancorada emhttps://) — aRewriteRuledo roteador e sua flag[B]são preservadas na cópia. Nenhuma correção necessária no instalador. -
[x] Fora do core e do ambiente local (git-ignored), as únicas ocorrências do padrão antigo estão em prompts HISTÓRICOS de
ai-workspace/(registro de uma tarefa passada sobre oRewriteBase), deliberadamente não reescritos.
-
-
[x] Correções de core (origem):
-
[x]
arquivo_nome_sanitizar()troca espaços por hífen na entrada (upload e renomeação) — arquivos novos nascem com nome URL-safe. -
[x]
urlDeArquivo()no motor codifica a URL devolvida pelo gerenciador (encodeURI(decodeURI(...)), idempotente) nos 3 pontos de montagem (image-picker do modal, fundo do styler e fonte do embed). O caminho continua cru como identificador, sem afetar a resolução no servidor.
-
-
[x] Streaming de mídia (
arquivo-estatico.php):-
[x]
Accept-Ranges: bytes+Content-Lengthem todas as respostas (antes eraTransfer-Encoding: chunkedsem tamanho). -
[x]
Rangeatendido com 206 Partial Content +Content-Range, enviando em blocos de 8 KB; faixa inválida responde 416. -
[x]
Content-Typesemcharsetpara binários (vídeo/áudio/imagem/PDF); formatos de texto (js/css/svg/json/html/xml) mantêmcharset=UTF-8. -
[x] Buffers de saída limpos antes do envio (binário íntegro e
Content-Lengthcorreto).
-
-
[x] Dimensionamento do áudio:
-
[x]
embedDefaultSize()por tipo: áudio sem altura, vídeo 360px, iframe/embed 400px, documento 600px (antes todo embed herdava 600px, esticando o player de áudio). -
[x]
buildMediaMarkup()nunca emiteheightpara<audio>;applyEmbedSize()redimensiona áudio só na largura.
-
-
[ ] Homologação runtime: pendente com o operador.
Evidência de Validação (BATCH-100)
Reportada pelo executor em 2026-07-30:
-
Verificação HTTP real no container
[REDACTED], após a correção:-
GETdo vídeo →HTTP/1.1 200,Accept-Ranges: bytes,Content-Length: 2069867,Content-Type: video/mp4(sem charset). -
GETcomRange: bytes=0-99→HTTP/1.1 206 Partial Content,Content-Range: bytes 0-99/2069867,Content-Length: 100. -
Range: bytes=99999999-→HTTP 416. -
interface.css→Content-Type: text/css; charset=UTF-8(texto preservado).
-
-
php -l→arquivo-estatico.php,arquivo.php,ArquivoEstaticoRangeTest.php→ 3/3 OK.node --checkno motor → OK. -
composer test(PHPUnit) → 165/165 (684 assertions, 4 skipped), com o novoArquivoEstaticoRangeTest6/6 (faixa fechada, aberta, sufixo, limite, inválidas→416 e Content-Type) e o caso novo de sanitização de espaços emAdminArquivosSegurancaTest. -
npx vitest run→ 66/66, com 2 casos novos (urlDeArquivoe dimensionamento do áudio). -
Cache-bust:
biblioteca-html-editor1.5.2→1.5.3, motor no Live Editor?v=c2f13→?v=c2f14.
Pendências
-
Runtime (após deploy
Update => Core): reproduzir vídeo e áudio na página publicada (inclusive arrastar a linha do tempo e testar em iOS/Safari); enviar um arquivo com espaço no nome e conferir que ele é gravado com hífen; conferir o player de áudio com a altura natural. -
Arquivos JÁ enviados com espaço no nome continuam dependendo do
.htaccesscom[B]— em instalações antigas, atualizar o arquivo ou renomear os arquivos pelo gerenciador. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-101 - Embed sem Arquivo Escolhido não Emite Atributo de Fonte Vazio (homologação, 2026-07-30)
-
[x] Sintoma:
<audio>inserido pelo painel "+" aparecia colapsado (~11px de largura, ~70px de altura = padding do badge + player em erro). -
[x] Diagnóstico por comparação dos dois HTMLs enviados pelo Chefe (um quebrado, um correto, com invólucros idênticos e
display: blocknos dois — descartando contexto de flex/grid como causa):-
[x] O quebrado tinha
src=""; o correto não tinha o atributo. -
[x] O quebrado trazia
position:relative; z-index:1com espaço após o;— assinatura dostyleExtrareconstruído emembedReadConfig(join('; ')), provando que ele passou pelo "Aplicar" do modal, enquanto o correto veio apenas dobuildElement. A section escolhida era coincidência. -
[x]
src=""faz o navegador resolver para a URL da própria página e tentar carregá-la como mídia: erro de decodificação, player colapsado e uma requisição inútil ao HTML.
-
-
[x] Correção: os geradores omitem o atributo de fonte quando vazio —
src(mídia/iframe),data(<object>),data-pdf-src(PDF.js) e osrcdo Google Viewer (que apontaria para um viewer comurl=vazia). -
[x] Hipótese descartada e revertida: chegou-se a aplicar
width:100%no invólucro de largura fluida (suspeita de contexto shrink-to-fit em flex/grid). A evidência mostrou invólucros idênticos nos dois casos, então a mudança foi revertida em vez de mantida "por precaução". -
[ ] Homologação runtime: pendente com o operador.
Evidência de Validação (BATCH-101)
Reportada pelo executor em 2026-07-30:
-
node --check gestor/assets/interface/html-editor.js→ OK. -
npx vitest run→ 68/68, com 2 casos novos: fonte vazia omite o atributo nos 4 geradores; fonte preenchida continua emitindo normalmente. -
composer test(PHPUnit) → 165/165 sem regressão. -
Cache-bust:
biblioteca-html-editor1.5.3→1.5.4, motor?v=c2f14→?v=c2f15.
Pendências
-
Runtime: inserir áudio/vídeo pelo painel "+", aplicar o modal SEM escolher arquivo e confirmar que o player mantém a largura normal; depois escolher o arquivo e confirmar a reprodução.
-
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-102 - Memória da Última Pasta no Modo Picker do Admin-Arquivos (demanda direta do Chefe, 2026-07-30)
-
[x] Diagnóstico: o cache da pasta corrente (
localStorage['adminArquivosDir']) era ignorado nas DUAS pontas quandopaginaIframeestava ativo — não era lido no boot (if (!cfg.paginaIframe)) nem gravado após navegar (BATCH-090). O seletor abria sempre na raiz. -
[x] Leitura: o cache passa a valer também no picker, com precedência
?dir=explícito > cache > raiz. -
[x] Gravação: a pasta corrente é salva em toda navegação, inclusive no picker (mesma árvore de arquivos).
-
[x] Contrato alinhado entre as telas: a listagem passou a expor
dirExplicito/dirInicial(a partir de?dir=, saneado porarquivo_caminho_relativo_seguro), exatamente como a tela de envio já fazia — as duas telas agora decidem a pasta inicial pela mesma regra, e um chamador pode abrir o seletor numa pasta específica. -
[x] Fluxo completo do picker coerente: navegar até a pasta → "Adicionar" leva
?dir=(explícito) → enviar → voltar pela seta cai na listagem semdir→ o cache devolve a mesma pasta. -
[x] Pasta apagada não quebra:
admin_arquivos_ler_pastajá cai para a raiz quando a pasta do cache não existe mais (self-heal do BATCH-090). -
[ ] Homologação runtime: pendente com o operador.
Evidência de Validação (BATCH-102)
Reportada pelo executor em 2026-07-30:
-
php -l gestor/modulos/admin-arquivos/admin-arquivos.php→ OK;node --check admin-arquivos.js→ OK;admin-arquivos.jsonválido. -
composer test(PHPUnit) → 165/165 sem regressão. -
Cache-bust: módulo
admin-arquivos1.1.6→1.1.7(o?v=dojs.jsvem da chaveversaodo manifesto). -
Sem cobertura automatizada: o comportamento é estado de front (localStorage) + renderização condicional; verificação manual registrada nas pendências, conforme a regra final do checklist.
-
Módulo espelhado no mirror local (
dev-environment/data/.../modulos/admin-arquivos/) para teste imediato antes do deploy.
Pendências
-
Runtime: navegar até uma subpasta pelo gerenciador normal, abrir o seletor por um campo de imagem/embed e confirmar que ele abre na MESMA pasta; navegar para outra pasta dentro do picker, fechar, reabrir e confirmar a persistência; conferir que o botão "Adicionar" (com
?dir=) continua abrindo na pasta corrente. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-103 - Filtro de Módulos no Menu Principal do Gestor (demanda direta do Chefe, 2026-07-30)
-
[x] Campo de filtro no menu: input
#gestor-menu-filtrono topo do componentemenu-principal-sistema(pt-br e en), com ícone de busca e placeholder já traduzido por idioma (o componente é versionado por idioma, dispensando texto condicional no PHP). -
[x] Comportamento espelhado da Editbar: itens que não casam somem; o bloco do grupo é ocultado junto com o cabeçalho quando fica sem itens visíveis.
-
[x] Busca tolerante: comparação ignora caixa e acentuação (
normalize('NFD')+ remoção das marcas combinantes) — 'usuarios' encontra 'Usuários', 'indice' encontra 'Páginas Índice'. -
[x] Itens fixos também filtrados (Dashboard e Sair), conforme decidido pelo Chefe; limpar o campo devolve o menu completo.
-
[x] Extras de usabilidade: aviso 'Nenhum módulo encontrado' (texto do componente, por idioma) e tecla Esc para limpar o filtro.
-
[x] JS em
gestor/assets/global/admin.js(arquivo indicado pelo Chefe, até então vazio), com API públicawindow.gestorMenuFiltro.aplicar/iniciareiniciar()idempotente. -
[x] Inclusão do asset: a tag
<script>acompanha o HTML retornado porgestor_pagina_menu(). A fila de assets NÃO funciona nesse ponto:gestor_pagina_menu()é chamada porgestor_pagina_variaveis()(gestor.php:619), que roda DEPOIS degestor_pagina_extra_head_e_javascript()— o marcador de JS já foi resolvido e o item enfileirado ficaria órfão. -
[x] Fonte 100% ASCII no regex de acentos: o range é montado com
String.fromCharCode, imune à normalização Unicode do arquivo por editores. -
[ ] Homologação runtime (deploy
Update => Core): pendente com o operador.
Evidência de Validação (BATCH-103)
Reportada pelo executor em 2026-07-30:
-
php -l gestor/gestor.php→ OK;node --check gestor/assets/global/admin.js→ OK. -
npx vitest run→ 76/76 (9 arquivos), com o novotests/Unit/JS/admin-menu-filtro.test.js8/8: API exposta, filtro por texto, ocultação do bloco/cabeçalho, tolerância a acento e caixa, filtragem dos itens fixos, aviso de vazio, reação ao eventoinput, Esc limpando einiciar()idempotente. -
composer test(PHPUnit) → 165/165 sem regressão.
Pendências
-
Deploy obrigatório: o componente
menu-principal-sistemaé lido do BANCO (gestor_componente), então o campo só aparece após o pipeline de resources doUpdate => Core. Oadmin.jsé asset físico e passa a ser servido assim que o core for sincronizado. -
Runtime: digitar no campo e conferir filtragem, ocultação de grupos vazios, aviso de vazio, Esc e o menu em
/en/. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
Rodada 2 do BATCH-103 — correções de consistência nas buscas (mesma sessão)
Reportado pelo Chefe na homologação: o filtro NOVO do menu encontrava itens acentuados, mas o filtro da Editbar — usado como base — não. A comparação foi então unificada.
-
[x] Editbar (
dashboard.iframe-toolbar.js):#c2f-modules-filterpassou a normalizar acento e caixa. Digitar 'pa' encontra 'Páginas' (antes oindexOfcru falhava no 2º caractere acentuado). -
[x] Busca de modelos (
html-editor-interface.js): mesmo defeito nomodelosFiltrar, corrigido com o helperhtmlEditorNormalizarBusca(aplicado a header, meta e id do modelo). -
[x] Botão 'x' de limpar na aba Modelos:
#modelos-search-inputera o único campo de busca sem o atalho; ganhou o par lupa/x no padrão do gerenciador de arquivos (.modelos-search-icon/.modelos-search-clear), nos componentes pt-br e en, com clique limpando + refiltrando + devolvendo o foco (Esc também sincroniza os ícones). -
[x] Salto de rolagem no 'Carregar Mais':
modelosCarregarescondia#modelos-cardsdurante o AJAX — a página perdia toda a altura da lista e o navegador voltava ao topo. Agora a lista só é escondida na PRIMEIRA página (quando é de fato substituída); na paginação o loading aparece abaixo dos cards, sem deslocar a leitura. -
[x] Helper duplicado deliberadamente nos três contextos (painel, iframe da Editbar e editor clássico), sempre com o range de acentos montado por
String.fromCharCode(fonte ASCII).
Evidência: node --check 3/3 OK; npx vitest run 83/83, com o novo tests/Unit/JS/dashboard.iframe-toolbar.test.js 5/5 (regressão do acento, termo acentuado, caixa, cabeçalho de grupo vazio e limpeza do campo) e 2 casos novos em html-editor-vars.test.js (normalização e alternância dos ícones); composer test 165/165. Cache-bust: dashboard.json 1.0.13→1.0.14, biblioteca-html-editor 1.5.4→1.5.5.
Pendência runtime: conferir na Editbar que 'pa' lista as Páginas; na aba Modelos, o 'x' limpando a busca e o 'Carregar Mais' mantendo a posição da rolagem.
BATCH-104 - Checkout Transparente e Tokenização PayPal (req-098, 2026-07-31)
-
[x]
paypal_gerar_client_token()chamaPOST /v1/identity/generate-token, aceitacustomer_idstring opcional, retorna apenasclient_tokene falha para payload/HTTP inválido. -
[x]
paypal_criar_pedido()preserva o fluxo anterior e acrescentapayment_source,intentCAPTURE/AUTHORIZE,Prefer: return=representationePayPal-Request-Idem chamadas one-step. -
[x] Respostas idempotentes 200 e criações 201 são aceitas em Orders.
-
[x]
paypal_criar_assinatura()recebe$params['payment_source']e o encaminha no nesting oficialsubscriber.payment_source. -
[x]
paypal_processar_pagamento_transparente()valida tipo, fonte e valor/plano antes da API e cobre captura pororder_id, ordem por token/vault e assinatura. -
[x] Versão PHP
3.1.0e novas funções expostas porpaypal_info(). -
[x] Loader JS deduplica chamadas, monta query do SDK e mantém o client token somente em
data-client-token. -
[x] Card Fields renderiza número, validade, CVV e titular, com seletores customizáveis e fallback para Hosted Fields legado.
-
[x] Eventos do SDK aplicam classes de foco/válido/inválido,
aria-invalide mensagens customizáveis. -
[x] Submit consulta
getState(), bloqueia formulário inválido e normalizaorder_id/payment_sourcesem converter dados mascarados em token fictício. -
[x] APIs globais:
paypalCarregamentoSDK,paypalCardFieldsInit,paypalCardFieldsSubmiteconn2flowPaypal. -
[x] Cobertura JS nova 7/7: query/atributo seguro, deduplicação, renderização, validação, retorno de fonte, retorno de ordem e bloqueio de submit.
-
[x] Cobertura PHP nova 7/7: fonte card/token, rejeições, helper, nesting, idempotência e metadados.
-
[ ] Homologação runtime com credenciais Sandbox e conta elegível (não automatizada para evitar chamadas financeiras externas).
Evidência de Validação (BATCH-104)
-
php -l gestor/bibliotecas/paypal.phpe no teste PHP: OK. -
node --check gestor/assets/paypal/paypal.jse no teste JS: OK. -
git diff --check: OK. -
npx vitest run: 90/90 em 11 arquivos;paypal.test.js7/7. -
composer test: 172/172, 707 assertions e 4 testes preexistentes pulados;PaypalTransparentCheckoutTest7/7. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
Pendências
-
Em Sandbox: conferir elegibilidade, estados visuais, captura, autorização e 3DS quando aplicável.
-
Assinatura transparente por cartão depende do produto, região e conta habilitados pelo PayPal; validar antes de Live.
-
Revisar CSP da aplicação hospedeira para os domínios exigidos pelo SDK/iframes PayPal.
BATCH-105 - Navegacao por Teclado nos Filtros de Modulos (2026-08-03)
-
[x]
ArrowDownno input foca o primeiro resultado visivel nos dois menus. -
[x]
ArrowDown/ArrowUppercorrem somente resultados visiveis. -
[x]
ArrowUpno primeiro resultado devolve o foco ao input correspondente. -
[x]
ArrowDownno ultimo resultado nao cria ciclo. -
[x] Sem resultados, o foco permanece no input.
-
[x] Enter permanece sob o comportamento nativo do link focado.
-
[x] Escape do filtro do menu principal continua limpando o termo.
-
[x] Estrutura PHP e componentes por idioma permanecem compativeis e sem texto novo.
-
[ ] Homologacao manual na Editbar e no menu principal apos sincronizacao do core.
Evidencia de Validacao (BATCH-105)
-
node --check gestor/modulos/dashboard/dashboard.iframe-toolbar.js: OK. -
node --check gestor/assets/global/admin.js: OK. -
Parse de
gestor/modulos/dashboard/dashboard.json: OK. -
Testes focados (
dashboard.iframe-toolbar.test.js+admin-menu-filtro.test.js): 16/16. -
Suite Vitest completa: 93/93 em 11 arquivos.
-
git diff --checkno slice: OK. -
Avisos de rede do Happy DOM em
html-editor-embed.test.jssao preexistentes; nao alteram o resultado aprovado da suite nem exercitam os arquivos deste batch.
BATCH-106 - Opções de Exibição, Sidebar de CSS, Barra de Navegação e Ação Substituir (req-106, 2026-08-06)
-
[x] Painel de Opções de Exibição (
c2f-view-options-panel):-
[x] Botão
c2f-tb-view-optionsna Editbar (c2f-toolbar-editbar, pt-br e en) e na topbar do editor visual dos módulos (componentehtml-editor-visual-modal, pt-br e en). -
[x] Painel flutuante no padrão do
c2f-add-panel, com os toggles nomeadosSidebar Lateral de CSSeBarra de Navegação de Elementos. -
[x] Os dois nascem desativados; a escolha é persistida (
localStorage['c2f-he-view-options']) e recuperada no boot do motor, com o painel refletindo o estado real ao abrir.
-
-
[x] Sidebar Lateral Fixa de CSS (
c2f-he-css-sidebar):-
[x] Coluna fixa à esquerda de 240px, cabeçalho fixo "Sidebar Lateral de CSS" e corpo rolável na altura útil da viewport.
-
[x] Encaixa abaixo da Barra de Navegação e do offset da Editbar, sem sobreposição (
top= offset + altura da barra;height: calc(100vh - top)). -
[x] O
#html-editor-tailwind-styleré realocado para dentro dela (mesmo nó/handlers, sem duplicação) e devolvido aodocument.bodyao desligar. -
[x] Classes Tailwind ativas agrupadas por variante (
base,sm:,md:,hover:…) com remoção pelo "x". -
[x] Bloco dedicado às classes customizadas do projeto (não-Tailwind), também removíveis.
-
[x] Autocomplete instantâneo sobre o dicionário expandido (a partir de 2 caracteres do último token).
-
[x] Seção "Valores manuais" no
he-styler-col-visualaceitando25px,1.5rem,#123456(vazio remove a propriedade). -
[x] Campo de CSS inline customizado lendo/gravando o atributo
styleinteiro. -
[x] Inspetor de estilos computados (
getComputedStyle, 20 propriedades). -
[x]
tailwindSuggestionsexpandido (flexbox, grid, tamanhos, espaçamentos, bordas, sombras, opacidade, posicionamento, transições, paleta completa, arbitráriosw-[350px]/bg-[#1a2b3c]e variantes) alimentando datalist e autocomplete.
-
-
[x] Barra Superior Fixa de Navegação (
c2f-he-element-navbar):-
[x] 44px abaixo da Editbar, colunas 20% (rótulo fixo) / 80% (área útil).
-
[x] Breadcrumb de ancestrais e lista de filhos realocados para a coluna de 80% e devolvidos ao
document.bodyao desligar. -
[x] Desativada por padrão.
-
-
[x] Toolbar flutuante e ação "Substituir" (
.he-tb-replace):-
[x] O toolbar contextual continua acompanhando o elemento selecionado.
-
[x] Botão ao lado de Copiar/Colar, visível só com cópia guardada e elemento selecionado.
-
[x] Substitui o elemento pelo bloco da área de transferência, renumera ids de widget e seleciona automaticamente o novo objeto.
-
-
[x] Isolamento da UI nova:
isEditorOwned(clique não vaza para o conteúdo atrás),extractUserHtmle o fallback de salvamento dohtml-editor-interface.jsremovem os painéis;disable()/enable()escondem e devolvem os painéis fixos. -
[ ] Homologação runtime (deploy
Update => Core): pendente com o operador — os dois botões vêm de página/componente lidos do banco.
Evidência de Validação (BATCH-106)
Reportada pelo executor em 2026-08-06:
-
node --check→html-editor.js,html-editor-interface.js,html-editor-visual-controls.js,dashboard.toolbar.js,dashboard.iframe-toolbar.js→ 5/5 OK. -
php -l gestor/bibliotecas/html-editor.php→ OK. Parse dedashboard.json,resources/pt-br/components.jsoneresources/en/components.json→ OK. -
npx vitest run→ 118/118 em 12 arquivos (antes 93/93), com o novotests/Unit/JS/html-editor-view-options.test.js21/21 (painéis desligados por padrão, encaixe/desencaixe dos três painéis, persistência entre instâncias, encaixe sem sobreposição com offset da Editbar,disable/enable, isolamento na extração, aviso de vazio, chave desconhecida, botão e comportamento do Substituir, renumeração de ids, separação Tailwind × customizadas, variantes, autocomplete, valores manuais, CSS inline, computados e dicionário expandido), 3 casos novos emdashboard.toolbar.test.js(montagem, sincronização e tradução do painel) e 1 emdashboard.iframe-toolbar.test.js(mensagem do botão da Editbar). -
composer test(PHPUnit) → 172/172 (707 assertions, 4 skipped preexistentes) sem regressão. -
Ajuste de teste existente:
html-editor.live.test.jspassou a mirar#html-editor-tailwind-styler .he-class-input— o styler deixou de ter um único<input>(os campos de valores manuais também são inputs). -
Sem cobertura automatizada: o painel da janela pai (
html-editor-visual-controls.js) depende de jQuery/Fomantic reais; o stub de teste do repositório não cobreis/css/outerWidth. Verificação registrada como manual, conforme a regra final do checklist. -
Cache-bust:
biblioteca-html-editor1.5.6→1.5.7,dashboard.json1.0.15→1.0.16, motor no Live Editor?v=c2f15→?v=c2f16. Checksums dos recursos alterados esvaziados para o pipeline recalcular.
Pendências
-
Deploy obrigatório: a página
dashboard-site-toolbar(Editbar) e o componentehtml-editor-visual-modal(editor dos módulos) são lidos do BANCO — os botões só aparecem após oUpdate => Core. Os assets JS são físicos e passam a valer com o core sincronizado. -
Runtime: ligar/desligar os dois toggles nos dois editores; conferir a persistência após recarregar; conferir o encaixe sem sobreposição; testar valores manuais, autocomplete, CSS inline e computados; testar Substituir (copiar A, selecionar B, substituir → B vira A e fica selecionado); salvar e conferir que o HTML persistido não contém a UI dos painéis; repetir em
/en/. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
Rodada 2 do BATCH-106 — homologação do Chefe (mesma data)
Quatro ajustes de usabilidade reportados no teste da rodada 1:
-
[x] Painéis flutuantes legados aposentados: trilha de ancestrais, lista de filhos e caixa de estilização não acompanham mais o elemento selecionado — existem apenas dentro da Barra de Navegação e da Sidebar de CSS. Com o painel correspondente desligado, nada é exibido (o empilhamento flutuante segue no arquivo, inativo). A barra flutuante de AÇÕES continua acompanhando o elemento.
-
[x] Botão de fechar (
c2f-he-panel-close) no canto superior direito da área do título dos dois painéis: desliga o painel, exatamente como desmarcar o toggle nas Opções de Exibição (o painel de opções reflete o estado ao reabrir). -
[x] Botão de ancoragem (
c2f-he-panel-side) à esquerda do fechar: setas horizontais na sidebar (esquerda ⇄ direita) e verticais na barra de navegação (topo ⇄ base). -
[x] Ancoragem persistida com o padrão original:
cssSidebarRight/elementNavbarBottomno mesmo registro delocalStorage, ambosfalse(sidebar à esquerda, barra no topo). O encaixe é recalculado nos quatro arranjos — com a barra embaixo, a sidebar sobe até a Editbar e encurta na base. -
[x] Clique na Editbar fecha TODA a UI flutuante: o clique ocorre dentro do iframe da barra, então o
mousedownda página hospedeira nunca disparava e nenhum backdrop era atingido. A barra publicac2f-toolbar:ui-dismissa cadamousedown(em captura) e o [REDACTED] comdismissHostPanels()— painéis desta página (Opções de Exibição, "+", Backups) e, por delegação ac2fEditor.dismissFloatingUi(), os do motor (Modelos, IA, Código Customizado, modal de embed, seletor de arquivos e modal de edição). É o mesmo conjunto que já fechava ao clicar fora na área editável. -
[x] Clique DENTRO de painel/modal não fecha nada: o aviso só nasce de cliques na barra; na área editável cada backdrop/
closest()continua responsável. Como omousedownprecede oclick, o botão que abre um painel continua funcionando. -
[x] Regressão corrigida:
closeEmbedModal()zeraisModalActiveincondicionalmente — lido depois dele, o modal de edição nunca seria fechado pordismissFloatingUi. O estado passou a ser lido antes, e embed/picker só são fechados quando de fato estão abertos.
Evidência (2026-08-06): node --check em html-editor.js, dashboard.toolbar.js e dashboard.iframe-toolbar.js → 3/3 OK; php -l e parse do dashboard.json → OK; npx vitest run → 130/130 (html-editor-view-options.test.js 21→29: legado oculto, exibição ao ligar, fechar pelo cabeçalho, alternância esquerda/direita, alternância topo/base com reencaixe da sidebar, persistência da ancoragem, dismissFloatingUi fechando os painéis do motor sem perder a seleção e o guard do modal de edição; +3 em dashboard.toolbar.test.js — dismissHostPanels nos painéis do host, delegação ao motor e ausência de editor; +1 em dashboard.iframe-toolbar.test.js — aviso e ordem mousedown → click); composer test → 181/181 (a suíte PHP cresceu com o BATCH-107, em paralelo; sem regressão). Cache-bust: biblioteca-html-editor 1.5.7→1.5.8, dashboard.json 1.0.16→1.0.17, motor ?v=c2f16→?v=c2f17. Nenhum recurso do banco foi alterado nesta rodada.
Pendência runtime da rodada 2: conferir que nada flutua com os painéis desligados; alternar a ancoragem nos quatro arranjos; fechar cada painel pelo ✕; abrir cada painel (Opções, "+", Backups, Modelos, IA, Código Customizado) e clicar na Editbar confirmando o fechamento; conferir que clicar dentro do painel não o fecha.
Rodada 3 do BATCH-106 — homologação do Chefe (mesma data)
-
[x] CSS inline com CodeMirror: o campo
he-inline-cssda Sidebar de CSS virou editor CodeMirror (mode: css, tematomorrow-night-bright,indentUnit4, 110px — a largura da sidebar não comporta os 800px do padrão de abas), criado sob demanda apenas com a sidebar ligada e idempotente (dedup por.CodeMirrorirmão). -
[x] Degradação graciosa preservada: sem a biblioteca (ou sem
getValue/setValue), o<textarea>continua funcionando; o listener deblursó é registrado se a build expuseron(). -
[x] Leitura/escrita coerentes:
syncInlineCssusasetValue+refresh(o editor pode nascer oculto) eapplyInlineCsslê deinlineCssValue(); valor idêntico aostyleatual não empilha passo de undo. -
[x] Crescimento dinâmico do editor preservado: arrastar o canto inferior direito continua aumentando/diminuindo o CodeMirror junto com a caixa (comportamento do BATCH-081).
-
[x]
#c2f-ai-statusdeixa de sumir ao redimensionar a caixa: o cálculo descontava só o topo do editor, fazendo o CodeMirror tomar o espaço de quem vinha depois. Agora descontaalturaAposElemento(editor, corpo)— irmãos posteriores do editor e dos seus ancestrais até o corpo — com folga de 8px. -
[x] Editor da aba "Modo" nasce na altura certa: o ajuste vivia apenas no
ResizeObserver(que reage a mudanças da CAIXA). Extraído parasyncLiveBoxCodeMirrors(panel)e chamado também na troca de abas e na abertura dos painéis de IA e Código Customizado. -
[x] Correção dentro da própria rodada: a primeira tentativa usou
clientHeight − (scrollHeight − altura dos editores). ComoscrollHeightnunca é menor queclientHeight, com o conteúdo cabendo a conta virava "altura atual − folga" e o editor encolhia a cada disparo até o mínimo, deixando de acompanhar o mouse. A conta voltou a partir do fundo do corpo e o desconto passou a independer da altura do editor (sem realimentação; guarda anti-loop por igualdade exata). -
[x] Painéis fixos permanecem no preview de dispositivo:
enterDevicePreview()chamadisable(), que escondia a Sidebar e a Barra.disable({ manterPaineis: true })é usado só nesse caminho — trocar a largura de visualização não é sair do modo de edição. Sair da edição e salvar continuam escondendo os painéis.
Evidência (2026-08-06): node --check em html-editor.js e dashboard.toolbar.js → 2/2 OK; php -l e parse do dashboard.json → OK; npx vitest run → 137/137 (html-editor-view-options.test.js 29→36: conversão em CodeMirror idempotente, leitura/escrita do style e aplicação no blur sem undo redundante, degradação graciosa sem a lib, painéis mantidos no preview de dispositivo e três casos de resize com medidas stubadas — desconto do status, acompanhamento do arraste nos dois sentidos e ausência de encolhimento espontâneo em disparos repetidos); composer test → 181/181 sem regressão. O stub de CodeMirror dos testes (tests/Unit/JS/setup.js) ganhou on() e um __emit() auxiliar. Cache-bust: biblioteca-html-editor 1.5.8→1.5.9, dashboard.json 1.0.17→1.0.18, motor ?v=c2f17→?v=c2f18. Nenhum recurso do banco foi alterado nesta rodada.
Pendência runtime da rodada 3: editar CSS inline pelo CodeMirror (botão e saída do campo); no Assistente IA, arrastar o canto inferior direito conferindo que o #c2f-ai-status continua visível e abrir a aba "Modo" conferindo a altura inicial do editor; trocar desktop/tablet/mobile conferindo que os painéis fixos permanecem.
BATCH-108 — Desacoplar a linha 2.x do código PHP 8.5 (req-108)
Contexto: o deploy falhava com HTTP 429 "Rate limit excedido" e a tabela api_rate_limits vazia. Causa: api_rate_limit_check() carregava banco-v2.php (sintaxe PHP 8.5) dentro de um try; o require_once lançava ParseError — que é Throwable — o catch devolvia false e o roteador traduzia isso para 429.
-
[x] Causa-raiz reproduzida no container:
try { require 'banco-v2.php'; } catch (Throwable $e)→CAPTURADO: ParseError :: syntax error, unexpected identifier "with". -
[x] Ambiente confirmado em PHP 8.3.32 (
apache2handlere CLI);banco-v2.phpnão compila em nenhuma branch (main/2.9.xlinha 173,3.0.xlinha 188). -
[x] Defeito confirmado nas três branches (mesmo
api.php, mesma linha 87) — não era problema da branch de desenvolvimento. -
[x]
api_rate_limit_check()reescrita: contagem viabanco.php(v1), semPHP_VERSION_ID, sem fallback, sem condicional — a linha 2.x não conhece PHP 8.5. -
[x] Três desfechos separados:
true(dentro do limite),false(excedido → 429) enull(falha de infraestrutura → 503 com log da exceção original, classe, arquivo e linha). -
[x] Contagem validada contra o banco real: chamadas consecutivas devolveram 1, 2, 3;
total <= maxdeutruecommax=100efalsecommax=2(429 legítimo preservado). Registros de teste removidos ao final. -
[x] Removidos da linha 2.x:
bibliotecas/banco-v2.php,bibliotecas/interface-v2.php,modulos/admin-paginas-v2/(2 arquivos) e os registros'interface-v2'/'banco-v2'emconfig.php. -
[x] Dados de seed órfãos do módulo removido eliminados pelo gerador (
atualizacao-dados-recursos.php), não à mão: Páginas 264→256, Variáveis 1668→1610, Componentes 120→118. Diff de 717 deleções e 1 inserção (apenas ogenerated_atdeschema-metadata.json); gerador reportou "Nenhum problema detectado" e nenhum órfão. -
[x]
bibliotecas/ftp.php:139: parêntese excedente removido (defeito independente, mesmo sintoma). -
[x] Lint completo do core sob PHP 8.4 (excluindo
vendor/,temp/, PHPMailer): 100% dos arquivos compilam — antes, 4 falhavam. -
[x] Nenhuma ocorrência de
PHP_VERSION_IDou80500introduzida emapi.php. -
[ ] Deploy
Update => Corena instalação local e reexecução dedeploy-project-v2.sh --project [REDACTED]-local --contents Sim(pendente operador) — oapi.phpque roda vem da instalação, não do repositório. -
[ ] Verificar após o deploy que
api_rate_limitspassa a acumularrequest_countna janela corrente (pendente operador). -
[ ] Linhas órfãs de
admin-paginas-v2nos bancosconn2flow/[REDACTED](8 páginas + 58 variáveis) não foram deletadas; a cargo do próximo sync ou de limpeza manual (pendente operador). -
[ ]
3.0.xpermanece intocada, combanco-v2e requisito de PHP 8.5 (por decisão; nada a validar aqui).
BATCH-109 — Cookies, Isenção de Crawlers, Auditoria de CSRF e Correções do Editor Visual (req-109, 2026-08-13)
Módulo 1 — Crawlers e cookie silencioso
-
[x]
gestor_crawler_detectar()reconhece WhatsApp,facebookexternalhit,meta-externalagent, Twitterbot, LinkedInBot, TelegramBot, Discordbot, Slackbot, Pinterest, Googlebot, bingbot, YandexBot e mais (29 tokens), ignorando caixa. -
[x] Navegador humano (Chrome/Safari/Firefox, desktop e iOS) não é classificado como bot; User-Agent ausente/vazio/não-string também não.
-
[x] Crawler não recebe cookie de verificação nem redirecionamento —
gestor_cookie_verificacao()retorna antes de qualquer efeito. -
[x] Página pública recebe o cookie por
Set-Cookiena PRÓPRIA resposta, semLocation: _gestor-cookie-verify/. -
[x] O redirecionamento sobrevive apenas onde a sessão é exigida:
gestor_permissao()(fora de AJAX) e signin/signup emperfil-usuario.php. -
[x] O valor emitido é registrado em
$_COOKIEno processo — segunda chamada no mesmo request não reemite outro token. -
[x]
setcookieprotegido porheaders_sent(). -
[x] Crawler que caia em
_gestor-cookie-verify/volta para a URL de origem, e não paracookies-is-mandatory/.
Módulo 2 — Páginas de sistema sem rastreamento
-
[x]
gestor_pagina_sistema_sem_rastreamento()reconhececookies-is-mandatory,_gestor-cookie-verify,404,403,500e503(exato ou prefixo de segmento), ignorando caixa e barra inicial. -
[x] Página de conteúdo com nome parecido (
cookies-is-mandatory-explicacao/,blog/artigo-404-…/) não é afetada. -
[x]
gestor_rastreamento_remover()retira<script>/<noscript>/<iframe>de GTM, GA e Meta Pixel e preserva o resto (meta, link, script do próprio site, iframe de vídeo). -
[x] Nas páginas de sistema,
project-javascripté zerado e as três filas (html-extra-head,javascript,javascript-fim) passam pelo filtro. -
[x]
global.jsneutralizafbq,dataLayer.pushegtagquandogestor.rastreamentoBloqueadoestá ligado, e não sobrescreve coletor já carregado pela página. -
[ ] Fora do alcance do core: a deduplicação do ID do Meta Pixel (
Duplicate Pixel ID) no snippet do projeto — não há nenhuma linha de GTM/Pixel no repositórioconn2flow(varredura emgestor/,conn2flow-sitee[REDACTED]); o snippet vive no banco/JS do deploy.
Módulo 3 — Logs e warning do editor
-
[x]
entrypoint.sh:chown/chmod 777nos diretórioslogsde[REDACTED]e criação degestor/logspara sites que não o tenham. -
[x]
gerenciar-sites.sh criarjá nasce comgestor/logsgravável e alinha permissões dentro do container quando ele está de pé. -
[x]
log.phpcria diretórios com0777+@chmoddefensivo e aplica0666na criação do arquivo. -
[x] Falha de escrita não imprime aviso no meio do HTML — vai para
error_loge a função retorna. -
[x]
html_editor_ia_prompt():defaultdo switch sem a referência a$modelo_texto(variável inexistente); nenhum modo de IA do repositório usa o marcador<!-- publisher -->que o resíduo tentava remover.
Módulo 4 — CSRF
-
[x] Causa-raiz confirmada:
$(form).submit()usa a propagação simulada do jQuery e cai emHTMLFormElement.prototype.submit(); nenhum dos dois dispara eventosubmit, então o listener nativo de captura nunca era acionado no salvamento do Editor Visual. -
[x]
global.jscobre os três caminhos: captura nativa, handler delegado do jQuery e envelope do prototype (instalado uma única vez — segunda carga do asset não empilha outro). -
[x] Token lido de
<meta name="csrf-token">,gestor.csrfTokeneparent.gestor.csrfToken(iframesrcdoc). -
[x] Formulário GET, de outra origem, ou sem token disponível não recebe campo; campo existente é atualizado, nunca duplicado.
-
[x]
moduloUrl()eliminaadmin-paginas/editar//nas três montagens do editor, sem colapsarhttps://. -
[x]
renderWidgetsnosrcdocenviaX-CSRF-Tokenno cabeçalho e_csrf_tokenno corpo; sem token, dispara sem campo vazio (o backend decide). -
[x]
previsualizarConfirmaranexa o token e, sem token, bloqueia o envio com aviso amigável em pt-br/en. -
[x]
gestor_csrf_resposta_invalida()preserva o JSON para AJAX e devolve página HTML ("Sessão expirada" + voltar) em navegação normal. -
[x] Campo mantido como
_csrf_token(contrato do BATCH-107 emseguranca_csrf_token_requisicao()), e não_gestor-csrf-tokencomo cita o intake — desvio registrado em DEC-104.
Módulo 5 — OpenGraph
-
[x] As seis tags são montadas com escape de aspas/
&e normalização de espaços/quebras. -
[x] Valor vazio não vira tag (um
og:imagevazio faz o WhatsApp exibir card sem imagem). -
[x]
twitter:cardacompanha:summary_large_imagecom imagem,summarysem. -
[x] Página/layout com OpenGraph próprio no
html_extra_headnão recebe o conjunto do core (gestor_open_graph_existe). -
[x] Fallback gracioso:
pagina#og→ nome/título da página →config.php(site-name, novassite-descriptionesite-og-image); imagem relativa é absolutizada comurl-full-http. -
[x] Injeção pulada em
paginaIframe. -
[x] Crawler sem sessão em página protegida recebe
200com apenas<head>(título +noindex+ OpenGraph), sem incluir o módulo da página; humano sem login continua indo para/signin/.
Evidência de Validação (BATCH-109)
Reportada pelo executor em 2026-08-13:
-
php -l→gestor/gestor.php,gestor/bibliotecas/gestor.php,gestor/bibliotecas/log.php,gestor/bibliotecas/html-editor.php,gestor/config.php,gestor/modulos/perfil-usuario/perfil-usuario.php→ 6/6 OK. -
node --check→gestor/assets/global/global.js,gestor/assets/interface/html-editor-interface.js→ 2/2 OK. -
bash -n→dev-environment/docker/entrypoint.sh,dev-environment/docker/gerenciar-sites.sh→ 2/2 OK. -
git diff --check→ OK. -
composer test(PHPUnit) → 200/200 (816 assertions, 4 skipped pré-existentes), com o novoCrawlersOpenGraphTest15/15. -
npx vitest run→ 171/171 em 15 arquivos (antes 143/143), com os novosglobal-csrf.test.js14/14 ehtml-editor-csrf-url.test.js14/14. -
Ajuste em teste existente: o stub de jQuery de
global-auth-redirect.test.jsganhouon()— oglobal.jspassou a registrar um handler delegado desubmit. -
Avisos de rede do Happy DOM em
html-editor-embed.test.jssão pré-existentes (registrados desde o BATCH-105) e não exercitam arquivos deste batch.
Pendências
-
Deploy
Update => Core+ homologação runtime (operador):-
compartilhar uma URL pública no WhatsApp/Telegram/Twitter e conferir que o card traz título, descrição e imagem (e não a página de cookies obrigatórios);
-
abrir uma página pública em janela anônima e conferir que não há 302 para
_gestor-cookie-verify/, mas o cookie chega noSet-Cookieda resposta; -
conferir que login/signup continuam fazendo o round-trip de cookie e que a área restrita segue exigindo sessão;
-
compartilhar URL de página protegida e conferir o card sem conteúdo privado; abrir a mesma URL como humano deslogado e conferir o
/signin/?url=…; -
abrir
cookies-is-mandatory/e conferir no DevTools que nenhuma requisição a[REDACTED]/[REDACTED]acontece; -
no Editor Visual: renderizar widgets no preview (sem 403) e salvar uma página (sem JSON cru na tela);
-
conferir a gravação dos logs pelo Apache e pelo CLI após recriar o container.
-
-
Configuração opcional: definir
SITE_DESCRIPTIONeSITE_OG_IMAGEno.envdo projeto para o fallback de compartilhamento. -
Observação não alterada: todos os
setcookiedo core usam'secure' => true. Em HTTP puro o navegador descarta o cookie; a verificação silenciosa remove o impacto em páginas públicas, mas os fluxos de autenticação sobre HTTP continuam dependendo de HTTPS. Mudar isso é decisão de postura de segurança do Chefe. -
Restrição respeitada: nenhum
git commit/git pushexecutado.
BATCH-110 — Metadados da Página, Imagem de Destaque, UI na Editbar/Editor e Sitemap XML (req-110, 2026-08-13)
Módulo 1 — Colunas e CRUD
-
[x] Migração
20260813120000_add_seo_metadata_to_paginas.phpacrescentaimagem_destaque(varchar 500),og_titulo(varchar 255) eog_descricao(text), comhasColumnidempotente edown()simétrico. -
[x] Grava-se o CAMINHO do arquivo, não o
id_arquivos— coerente com a árvore física do BATCH-090 e já aceito peloimagepick. -
[x]
admin-paginas: as três colunas entram emadicionar,editareclonar(campos de banco, gravação e leitura). -
[x] Na edição, limpar um campo GRAVA vazio e devolve o fallback (comparação sobre o valor do request, não
isset). -
[x] O roteador seleciona as colunas na montagem da página completa e preenche
$_GESTOR['pagina#og']. -
[x]
gestor_pagina_og_do_registro()devolve apenas chaves preenchidas — chave presente e vazia venceria o nome da página emgestor_open_graph_dados(). -
[x] Registro sem as colunas novas (base ainda não migrada) não quebra: devolve array vazio.
Módulo 2 — Aba "SEO & Compartilhamento"
-
[x] Componente
html-editor-seocriado em pt-br e en, com título social, descrição social e o placeholder da imagem. -
[x] A aba entra no
html-editorentre blocos removíveis; sem o parâmetroseo, menu e conteúdo são removidos e o conjunto de abas fica idêntico ao de antes (layouts, componentes, demais alvos). -
[x] A imagem usa o
imagepickdeinterface_formulario_campos()— nenhum código novo de seletor foi escrito. -
[x] 9 variáveis novas em
variables.json(pt-br/en); componente registrado comversion 1.0e checksums vazios;html-editorcom checksums esvaziados e versão bumpada (pt-br 1.24→1.25, en 1.10→1.11). -
[x] Diffs dos JSON de recursos limpos (sem reescrita em massa e preservando o escape
\/dojson_encodedo PHP nodashboard.json).
Módulo 3 — Botão de Configurações da Página na Editbar
-
[x] Botão
c2f-page-config-btnao lado doc2f-ai-btnnos dois idiomas. -
[x] A Editbar posta
c2f-toolbar:page-configcom posição epage_id; o painel é montado na página hospedeira. -
[x] O painel carrega os valores atuais, mostra a imagem quando existe e avisa quando não existe.
-
[x] "Escolher…" abre o
admin-arquivosem overlay; a seleção chega pelo contrato do picker do editor e vira a imagem de destaque. -
[x] Seleção recebida com o seletor FECHADO é ignorada (não sequestra outro picker da página).
-
[x] "Remover" limpa só a imagem, preservando o que já foi digitado.
-
[x] "Salvar" envia POST com
page_id,og_titulo,og_descricaoeimagem_destaque, e confirma na interface. -
[x] Erro do backend aparece para o usuário; falha ao carregar não deixa o painel travado em "Carregando".
-
[x] Clique na Editbar fecha o painel, EXCETO com o seletor de arquivos aberto.
-
[x] Endpoints atrás de
gestor_acesso('editar','admin-paginas')e do isolamento multiusuáriodashboard_site_toolbar_verificar_permissao_pagina(); caminho da imagem saneado porarquivo_caminho_relativo_seguro().
Módulo 4 — Sitemap XML
-
[x] Biblioteca
sitemap.phpregistrada emconfig.php; arquivo gravado na raiz pública, servida direto pelo.htaccess(RewriteCond %{SCRIPT_FILENAME} !-f). -
[x] Elegibilidade exclui:
tipo != 'pagina',sem_permissaofalso,status != 'A', rotas utilitárias (cookies-is-mandatory,404,_gestor-cookie-verify, signin/signup/dashboard) e páginas fora da janela de publicação. -
[x] Data zerada do MySQL (
0000-00-00 00:00:00) não invalida a página. -
[x]
sitemap_xml_montar()produz XML válido (simplexml_load_string), comlastmodsó quando há data. -
[x]
upsertacrescenta URL nova, ATUALIZA a existente sem duplicar e preserva as demais entradas byte a byte. -
[x]
upsertsobre arquivo corrompido gera um sitemap novo e válido. -
[x] Remoção tira só a URL alvo, inclusive quando a URL tem querystring escapada (
&); URL inexistente não altera nada. -
[x] Acionamento: chamada direta em adicionar/editar/clonar e
callbackFunctionem status/excluir. -
[x] Falha de gravação não interrompe o CRUD (vai para o log).
Evidência de Validação (BATCH-110)
Reportada pelo executor em 2026-08-13:
-
php -l→gestor.php,config.php,bibliotecas/gestor.php,bibliotecas/sitemap.php,bibliotecas/html-editor.php,bibliotecas/log.php,admin-paginas.php,dashboard.php,perfil-usuario.phpe a migração → 10/10 OK. -
node --check→global.js,html-editor-interface.js,dashboard.toolbar.js,dashboard.iframe-toolbar.js→ 4/4 OK. Parse dedashboard.json,components.jsonevariables.json(pt-br/en) → OK. -
git diff --check→ OK. -
composer test(PHPUnit) → 223/223 (878 assertions, 4 skipped pré-existentes), com o novoSitemapTest19/19 e 4 casos novos emCrawlersOpenGraphTest. -
npx vitest run→ 181/181 em 16 arquivos, com o novodashboard.page-config.test.js10/10. -
Armadilha registrada no caminho: o stub de
fetchdo teste precisou casar o valor EXATO deajaxOpcao—site-toolbar-page-configé prefixo desite-toolbar-page-config-save, e o match por substring devolvia a resposta errada, mascarando o teste de erro.
Pendências
-
Migração obrigatória (operador): rodar o Phinx para criar as três colunas. Sem elas, o
SELECTdo roteador falha — a atualização de banco precisa vir ANTES do deploy do core. -
Deploy
Update => Core(operador): o componentehtml-editor, o novohtml-editor-seo, as variáveis e a páginadashboard-site-toolbarvêm do BANCO. -
Runtime, após migração + deploy:
-
editar uma página em
/admin-paginas/editar/, abrir a aba "SEO & Compartilhamento", preencher os três campos (escolhendo a imagem pelo gerenciador) e salvar; recarregar e conferir a persistência; -
conferir no HTML público que
og:title/og:description/og:imagerefletem os valores gravados, e que limpar os campos devolve o fallback doconfig.php; -
compartilhar a URL no WhatsApp e conferir o card com a imagem de destaque;
-
no Live Editor, abrir "Configurações", trocar a imagem pelo seletor, salvar e conferir a persistência sem sair da página;
-
conferir a criação do
sitemap.xmlna raiz e o acesso direto porhttps://<host>/sitemap.xml; -
criar, despublicar e excluir uma página conferindo entrada, saída e ausência de duplicatas no sitemap;
-
conferir que páginas do painel e páginas com permissão NÃO aparecem no sitemap.
-
-
Configuração opcional:
SITE_DESCRIPTIONeSITE_OG_IMAGEno.envcontinuam sendo o fallback global (BATCH-109). -
Fora de escopo, registrado: o formulário do
publisher-pagesnão recebeu a aba de SEO. As publicações gravam na MESMA tabelapaginas, então as colunas já valem para elas e o roteador já as lê; o painel da Editbar também as edita. Falta apenas a aba no CRUD daquele módulo — slice pequeno e isolado, melhor num batch próprio. -
Restrição respeitada: nenhum
git commit/git pushexecutado.