As decisões correntes mais recentes permanecem em
sdd/decisions/DECISION-LOG.md. Este arquivo guarda o histórico movido para manter o log corrente enxuto (≤10 itens).
DEC-051 - 2026-06-13 - accepted
Painel Auxiliar de Formatação Visual (req-037 / BATCH-037). Decisões de design:
-
Estrutura de Duas Colunas: Dividir
#html-editor-tailwind-stylerem duas colunas: a da esquerda mantendo a lista de tags e input autocomplete existentes; a da direita contendo o Visual Helper com botões interativos. -
Propriedades Mapeadas: O Visual Helper deve gerenciar alinhamento de texto (Esquerda, Centro, Direita, Justificado), espaçamento/padding (Nenhum, Pequeno, Médio, Grande), arredondamento de bordas (Reto, Leve, Médio, Redondo) e paletas de cores rápidas em formato circular para Texto e Fundo.
-
Sincronização e Limpeza de Conflitos:
-
Cada grupo de controles de formatação visual deve ser mutuamente exclusivo (ex: aplicar
bg-blue-500limpa outras classes com prefixobg-). -
Ao carregar ou atualizar o selecionado, ler as classes vigentes e adicionar a classe
.activeaos botões visuais correspondentes. -
Qualquer clique nos controles visuais recalcula as classes aplicadas, atualiza a lista de tags na esquerda e chama
afterDomMutation().
-
-
Empilhamento Responsivo: Se a largura do elemento selecionado for inferior a 400px, o painel do styler muda para o layout vertical
.he-styler-stacked(flex-direction: column) para manter a usabilidade em botões e blocos pequenos.
Notas de execução (BATCH-037): a limpeza de conflitos NÃO usa prefixo cego (bg-/text-), e sim lista fechada do grupo + regex específica de cor — text-(<cor>)-<shade>|text-white|text-black e bg-(<cor>)-<shade>|bg-white|bg-black|bg-transparent. Isso evita dois falsos positivos: (a) trocar a cor do texto NÃO remove o alinhamento text-left/center/... nem o tamanho text-lg/...; (b) trocar a cor de fundo NÃO remove utilitários de imagem como bg-cover/bg-center/bg-no-repeat. Os grupos de alinhamento/padding/bordas usam lista fechada (+ regex ^p-\d+$ e ^rounded(-.+)?$ para variantes digitadas pelo operador). O painel (buildHelperPanelHtml()) é estático (gerado uma vez de tailwindHelperConfig()); renderStyler() só alterna .active via syncHelperButtons(). O botão "Nenhum" do padding mapeia para p-0 (classe real). Defaults destacados quando nada do grupo está presente: alinhamento→text-left, bordas→rounded-none, fundo→bg-transparent.
DEC-052 - 2026-06-13 - accepted
Inversão e Expansão do Painel Auxiliar de Formatação Visual (req-038 / BATCH-038). Decisões de design:
-
Inversão de Colunas: Inverter a exibição das colunas do styler
#html-editor-tailwind-styler. A Coluna Esquerda passa a exibir os controles visuais interativos (Visual Helper), enquanto a Coluna Direita exibe as tags de classes aplicadas e o input autocomplete. -
Propriedades Estendidas: Adicionar 7 novos grupos ao Visual Helper:
-
Tamanho do Texto (
text-sm/base/lg/xl). -
Espessura do Texto (
font-normal/medium/bold). -
Margem Externa (
m-0/2/4/8). -
Espessura da Borda (
border-0/border/border-2/border-4). -
Cor de Borda (Paleta circular de 8 cores).
-
Opacidade (
opacity-100/75/50/25). -
Layout / Display (
block/inline-block/flex/grid).
-
-
Limpeza e Sincronização Estendidas:
-
Ampliar
applyHelperClass()para remover classes conflitantes do mesmo grupo usando regras específicas (ex: regex para margem^m-\d+$ou^m-[a-z]+$, cor de borda^border-(<cor>)-<shade>|border-white|border-black, e display/font-size/font-weight/opacity por lista fechada). -
Estender
syncHelperButtons()para detectar e destacar o estado ativo para todos os novos grupos.
-
-
Deslocamento da Toolbar na Borda Inferior: Se a barra flutuante for desenhada na borda inferior do elemento (quando não há espaço no topo), a posição inicial de empilhamento vertical do breadcrumb (
stackTop) é acrescida da altura da toolbar (toolbar.offsetHeight + 12), empurrando os elementos de navegação e formatação para baixo da barra e evitando sobreposições.
Notas de execução (BATCH-038): sob autorização do Engenheiro Chefe Humano para "ser criativo e criar mais ainda", o painel foi expandido para 20 grupos organizados em 4 seções (.he-helper-section): Texto (Alinhamento, Tamanho, Peso, Caixa de texto, Decoração, Cor), Layout (Exibição, Direção flex, Justificar, Alinhar itens, Gap), Caixa (Largura, Padding, Margem), Aparência (Fundo, Cantos, Borda, Cor da borda, Sombra, Opacidade) — os 7 pedidos + 8 extras. O styler ganhou max-height:72vh + scroll. A limpeza de conflitos combina três mecanismos em applyHelperClass(): lista fechada do grupo (derivada dos botões), cleanList (variantes de palavra isolada: displays/sombras/transform/decoração/flex-dir/justify/items/width) e cleanRe (cores, ^p-\d+$, ^m[xytblr]?-\d+$ que preserva mx-auto/min-h-*, ^gap(-[xy])?-\d+$, ^opacity-\d+$, ^border-\d+$). As cores de borda são renderizadas como anel (.he-helper-bordercolor, fundo branco + borda 3px) para distingui-las das cores de fundo. As classes de largura com barra (w-1/2) funcionam normalmente em classList/seletores de atributo. Ajuste pós-feedback: como 20 grupos deixaram o painel alto, as seções viraram um accordion (cabeçalho .he-helper-section clicável + .he-helper-section-body colapsável; uma seção aberta por vez, "Texto" por padrão), reduzindo a altura visual; o estado persiste entre seleções.
DEC-053 - 2026-06-14 - accepted
Melhorias Visuais e Funcionais do Editor HTML Visual (req-039 / BATCH-039). Decisões de design:
-
Nova Seção "Fundo" (Background) no Styler: Criar seção "Fundo" reposicionando a cor de fundo (
bgColor) de "Aparência" para ela. Adicionar controles para imagem de fundo: botão de ImagePicker (enviandohtml-editor-imagepick-openpara o pai), tratamento de retornohtml-editor-imagepick-selected(aplicando estilo inlinebackground-imagee gerenciando preview local via flagimagePickerTarget = 'background'), e grupos de botões Tailwind para repetição (bg-repeat/bg-no-repeatetc.), tamanho (bg-auto/bg-coveretc.) e posição (bg-center/bg-topetc.). -
Deseleção e Toggle de Seleção: Inserir botão de deselecionar na toolbar flutuante (
.he-tb-deselect, íconetimes circleouban) com estilo destacado. Implementar toggle de seleção no clique global: se o elemento clicado for exatamente o selecionado ativo (el === this.selectedElement), deselecioná-lo viaclearSelection(). Preservar teclaEsc. -
Preservação de Rolagem no Histórico (Scroll Sync): Armazenar a posição
scrollTopdo iframe do preview em cada snapshot daundoStack/redoStack(formato{ html, scrollTop }). Ao realizarUndo(Ctrl+Z) ouRedo(Ctrl+Y), restaurar a rolagem vertical (window.scrollTo) correspondente. -
Alinhamento e Wrapping dos Breadcrumbs: Ajustar o CSS de
#html-editor-selection-breadcrumbe#html-editor-selection-childrenparadisplay: flex; flex-wrap: wrap; white-space: normal;para permitir quebra de linha. No métodoupdateSelectionUI(), aplicar um clamp horizontal seleft + offsetWidthultrapassar os limites do viewport do iframe. -
Elemento Fantasma (Ghost Insert Cursor): No modo de inserção, desenhar e mover o elemento
insertGhostcom opacidade0.6e borda tracejada roxa/cinza seguindo o mouse. Destruir ao sair do modo de inserção. -
Highlight de Append Completo: Ao realizar arraste ou inserção, se não houver posição em linha (antes/depois) mapeada, mas o cursor estiver sobre um contêiner pai, destacar o contêiner de destino com uma borda amarela tracejada de 4 lados (
#html-editor-parent-highlight-overlay). -
Renderização de Widgets com Esqueleto Real (AJAX): Criar o endpoint AJAX
html-editor-widget-renderno backend que aceitasignaturedo widget, e chamawidgets_get(['id' => signature])com$_GESTOR['ajax'] = falsepara retornar o HTML real do widget. No visual editor, chamar este endpoint ao adicionar ou atualizar widgets e injetar o resultado no.conn2flow-widget-innerdo wrapper virtual.
Notas de execução (BATCH-039): (a) O iframe (srcdoc) não faz o AJAX direto — pede ao pai por postMessage (c2f-he:widget-render), o html-editor-visual-controls.js consulta html-editor-widget-render e devolve c2f-he:widget-rendered; cada wrapper recebe um data-widget-id para casar a resposta. (b) O preview renderizado NÃO vaza no save: o mockup original é preservado em data-widget-mockup e é ele (not the .conn2flow-widget-inner com o preview) que volta entre os comentários <!-- widgets#... --> em extractUserHtml. O endpoint valida a assinatura por regex ^[\w\-]+->[\w\-]+\(.*\)$ antes de chamar widgets_get. (c) O bgImage é um grupo de kind:'bgimage' (sem classe Tailwind) que aplica background-image inline; a regex de limpeza de bgColor (bgColorRe) não atinge bg-cover/bg-center/bg-repeat, então cor de fundo e propriedades de imagem coexistem. (d) Highlight de contêiner e placeholder em linha são mutuamente exclusivos (showDropIndicator); a inserção real usa insertAtTarget (não depende mais do placeholder estar no DOM). (e) Após undo/redo, rerenderVisibleWidgets() repinta o esqueleto dos widgets sem mockup.
DEC-054 - 2026-06-14 - accepted
Ajustes Finais no Pré-visualizador de Widgets e Elemento Fantasma do Cursor (req-040 / BATCH-040). Decisões de design:
-
Renderização de Widgets no Pré-visualizador: Injetar um script utilitário em
previewHtmlConteudo()(emhtml-editor-interface.js) para processar e renderizar dinamicamente os widgets do preview no#iframe-visualizacao-paginavia AJAX parahtml-editor-widget-render(com os parâmetros dewindow.parent.gestor). -
Elemento Real no Cursor Fantasma: Em
createInsertGhost(payload)(emhtml-editor.js), renderizar o elemento ou wrapper de widget real usandobuildElement()oubuildWidgetWrapper()(com renderização assíncrona) e anexá-lo como filho de#html-editor-insert-ghost. -
Estilo Limpo do Cursor Fantasma: Ajustar a estilização de
#html-editor-insert-ghostpara remover restrições de layout estritas que impeçam ou quebrem a exibição e estruturação interna do elemento real renderizado.
Notas de execução (BATCH-040): (a) O script do preview é uma função nomeada widgetPreviewBootstrap() injetada por (${fn.toString()})() — isso preserva as regex literais (evita o problema de \s→s que ocorreria escrevendo o script como template string) e mantém o código autocontido (usa só globals do iframe + window.parent.gestor). Injetado nos dois caminhos de previewHtmlConteudo (layout e padrão), antes de </body>. O contêiner usa display:contents para não introduzir caixa extra no fluxo. (b) O nó de widget dentro do ghost recebe data-widget-id e dispara requestWidgetRender, mas não vaza no save/snapshot: o ghost (#html-editor-insert-ghost) é isEditorOwned, então seu conteúdo não é filho direto do body nem entra em getUserContentNodes. (c) pointer-events:none foi reforçado também nos descendentes do ghost (#html-editor-insert-ghost *) para a caixa flutuante jamais interceptar o cursor.
DEC-055 - 2026-06-15 - accepted
Correções e Melhorias no Módulo publisher-index (req-041 / BATCH-041). Decisões de design:
-
Busca Tolerante a Acentuação:
-
Implementar a função utilitária
publisher_index_widget_unicode_escape()que converte caracteres não-ASCII (acentos) do termo pesquisado para suas representações de escape Unicode (u00xxe\u00xx). -
Modificar a query SQL para realizar buscas combinadas com
ORcobrindo o termo original e as versões Unicode escapadas, permitindo que a busca encontre registros mesmo com acentuação corrompida no banco de dados.
-
-
Decodificação de Unicode Corrompido:
-
Criar o helper
publisher_index_widget_corrigir_unicode()que detecta padrões de escape Unicode (\?u[0-9a-fA-F]{4}) em strings e os re-decodifica para UTF-8 nativo usando a funçãopack('N', hexdec($m[1]))emb_convert_encoding(). -
Aplicar esta decodificação ao título e campos dinâmicos ao buscar publicações do banco, garantindo exibição textual correta no widget renderizado.
-
-
Filtro Estrito de Publicações (INNER JOIN):
-
Alterar o
LEFT JOINda consulta de publicações paraINNER JOINentrepaginasepublisher_pages. -
Isso elimina do widget a renderização indesejada da própria página de índice que renderiza o widget (e qualquer outra página comum que compartilhe o mesmo
publisher_idmas não seja uma publicação cadastrada na tabelapublisher_pages).
-
-
Resolução de Métricas de Paginação:
-
Expor as variáveis globais de widget
[[page_count]]e[[page_total]]no backend PHP. -
Enviar a propriedade
totalcom a contagem total de publicações filtradas no JSON do retorno AJAX. -
No cliente JS (
publisher-index.widget.js), contar fisicamente os filhos renderizados na listagem e preencher o texto de seletores[data-page-count]e[data-page-total]dinamicamente no término de cada chamada AJAX.
-
-
Ajustes e Simplificação de Layout do CRUD:
-
Remover o input de quantidade máxima de itens (
#count) por ser redundante com a paginação do índice. -
Expandir os campos de Regra de Alimentação (
rule) e Ordenação (order_by) para 8 colunas cada. -
Mover
#manual-items-wrapperpara dentro do bloco de Regra de Curadoria, e corrigir os typos de barra invertida em suas tags HTML (div\ classe\div\).
-
-
Integrações com Editor HTML Visual:
-
Declarar
window.publisher_index_update_target_variablesno interface JS do editor. -
Atualizar
alvoUsaItemVars()para suportar o alvo'publisher-index'. -
Atualizar a simulação interna de itens no editor para ler de
#items_per_page(fallback 10) em vez de#count.
-
Notas de execução (BATCH-041): (a) banco_select mapeia a chave do array de retorno pela string literal de cada campo (via explode(',', ...)), então COUNT(*) AS total vira uma chave não-trivial — publisher_index_widget_contar_publicacoes() lê o valor com reset($row). (b) A cláusula de busca foi extraída para publisher_index_widget_clausula_busca() (reusada na listagem e na contagem) gerando p.nome LIKE para o termo literal + variantes u00xx/\u00xx; o banco_escape_field escapa a \ da variante com barra, casando o conteúdo corrompido no banco. (c) O bloco de métricas "Exibindo X de Y" foi adicionado aos templates físicos lista/grid (e aos novos) com [data-page-count]/[data-page-total] e os placeholders @[[page_count]]@/@[[page_total]]@; o JS conta os filhos da listagem ignorando .publisher-index-empty. (d) O branch de simulação do publisher-highlights foi unificado com publisher-index em publisherVariablesOrSimulation (conforme a origem do count: #count vs #items_per_page). (e) Pedido adicional do Engenheiro Chefe Humano nesta rodada: criados 2 novos modelos para publisher-index — publisher-index-timeline (Linha do Tempo, trilho vertical com marcadores) e publisher-index-agenda (cartões horizontais com data em destaque), em pt-br e en, usando exclusivamente variáveis garantidas + blocos do widget, registrados em publisher-index.json (version 1.0, checksums vazios para o pipeline). Versões dos recursos alterados incrementadas (módulo 1.0.0→1.1.0; templates/páginas 1.1→1.2); checksums recalculados pelo pipeline.
DEC-056 - 2026-06-15 - accepted
Controle de Métricas no publisher-index e suíte de testes MySQL (req-042 / BATCH-042). Decisões desta rodada:
-
Controle show_metrics como flag retrocompatível de schema: O campo vive in
fields_schema, com defaulttrueem adicionar/editar/clonar e no JavaScript administrativo. Registros antigos continuam exibindo métricas sem migração de dados. -
Bloco condicional próprio para métricas: As métricas usam os delimitadores
<!-- metrics < --> ... <!-- metrics > -->, processados pelo mesmo helperpublisher_index_widget_bloco_condicional()dos controles de busca, ordenação e carregar mais. -
Variável global
show_metrics: O renderer passa a resolver[[show_metrics]]/@[[show_metrics]]@como'true'ou'false', permitindo templates customizados que queiram refletir esse estado em atributos ou lógica visual. -
Teste integrado MySQL gated e banco dedicado: O teste de busca real só roda com
CONN2FLOW_RUN_DB_TESTS=1e exigeCONN2FLOW_DB_DATABASE=conn2flow_test. O setup cria/dropa apenas tabelas mínimas nesse banco dedicado, evitando qualquer impacto no banco de desenvolvimento. -
Correção no
LIKEpara barra invertida literal: A validação integrada revelou que\também é caractere de escape no padrãoLIKEdo MySQL. Para casar nomes gravados comoT\u00edtulo, a variante com barra precisa ter a barra dobrada no padrão antes debanco_escape_field().
Notas de execução (BATCH-042): implementado em publisher-index.php, publisher-index.js, publisher-index.widget.php, seis views CRUD, oito templates físicos e tests/Integration/PublisherIndexWidgetTest.php. Validação: php -l nos PHPs alterados, node --check no JS administrativo, composer test OK e teste MySQL dedicado contra conn2flow_test OK.
DEC-057 - 2026-06-15 - accepted
Curadoria Manual no publisher-index, Novos Templates com Imagem, Variável de Widget no CRUD e Suporte Inline no Editor (req-043 / BATCH-043). Decisões desta rodada:
-
Regra de alimentação manual resolvida em PHP: Quando
rule === 'manual'nofields_schema, a busca dinâmica é substituída pela curadoria explícita. Uma nova funçãopublisher_index_widget_lista_manual()consulta os itens com filtro restritivoAND p.id IN (...)(cada ID viabanco_escape_field), sem ORDER BY nem LIMIT no SQL, mapeia os registros porpage_ide reordena em PHP para respeitar exatamente a ordem deselected_items; a busca textual filtra em PHP (case-insensitive sobre título e campos custom, viapublisher_index_widget_item_casa_busca()) e a paginação é aplicada porarray_slice(offset, limit).buscar_publicacoes()econtar_publicacoes()ganham um dispatch no topo: sem busca, a contagem écount(selected_items)literal; com busca, conta os itens curados que casam;selected_itemsvazio retorna[]/0.render_inlineerender_ajaxpassamrule/selected_itemsadiante. A montagem de itens foi extraída parapublisher_index_widget_montar_itens()(compartilhada com a busca dinâmica). Retrocompat: registros semruleseguem emlatest(dinâmico). -
Novos modelos com imagem destaque: 4 templates físicos (
publisher-index-grid-imagem,publisher-index-lista-imagem, em pt-br e en), clonados dos modelos grid/lista existentes acrescentando<img src="@[[item#imagem]]@">(campo custom mapeável) e um resumo@[[item#resumo]]@, preservando os blocos do widget (search-input/sort-select/metrics/item/no-item/load-more) e utilitários Tailwind padrão. Registrados empublisher-index.jsoncomversion1.0 e checksums vazios (pipeline recalcula). -
Variável de Widget no CRUD dos 4 módulos: A aba
data-tab="hep-widget"ganhou, entre o bloco informativo e o editor CodeMirror, um campo "Variável do Widget" (input readonly#hep-widget-val+ botão#btn-copy-widget-val.tealcom íconecopy) e um header "Código HTML". O JS de cada módulo (updateWidgetCodeTab) passa a popular#hep-widget-valcom a assinatura em formato de variável[[widgets#MODULO->render({"grupo_slug":"SLUG"})]]e registra (uma vez, via delegação) o clique de copiar usandonavigator.clipboard.writeText()com fallback por elemento temporário e feedback visual i18n. -
Variáveis de widget inline no editor e pré-visualizador: No
renderWidgets()dewidgetPreviewBootstrap(html-editor-interface.js) e emconvertWidgetCommentsToWrappers()(html-editor.js), antes da varredura de comentários, ocorrências de[[widgets#...]]/@[[widgets#...]]@nodocument.body.innerHTMLsão convertidas em pares de comentários<!-- widgets#$1 < --><!-- widgets#$1 > -->(regex literal/@?\[\[widgets#(.+?)\]\]@?/gi, preservada pela injeção via.toString()), tornando-as renderizáveis (preview) e editáveis (wrappers). -
Correção de teste (req §5): O stub jQuery do Vitest (
tests/Unit/JS/helpers/jquery-stub.js) não implementava.children()/.not(), deixandopublisher-index.widget.test.js(do BATCH-041/042) vermelho no baseline. Adicionados os dois métodos (adição pura, sem alterar comportamento existente) — formalizado no req-043 §5. As versões dos módulos foram incrementadas (publisher-index1.1.0→1.2.0;menus1.0.2→1.0.3;galleries1.0.1→1.0.2;publisher-highlights1.2.0→1.2.1); asversionde páginas/templates ficam para o pipeline (que detecta mudança por checksum dos*Data.json, não pelaversiondo manifest). -
Prefixagem dinâmica de url-raiz em campos de imagem (req §6): Os campos custom em
publisher_pages.fields_valuesguardam caminhos relativos, mas os novos templates de imagem destaque precisam do caminho a partir da raiz. Comopublisher_pagesnão declara tipos, o tipo de cada campo é lido dofields_schemada tabelapublisher(fields[].{id,type}) por uma helper com cache estático por idioma+publicador (publisher_index_widget_tipos_campos_publicador/publisher_highlights_widget_tipos_campos_publicador). Empublisher-index,montar_itens($rows, $publisher_id = null)ganhou o 2º parâmetro (passado porbuscar_publicacoeselista_manual); empublisher-highlights, a prefixagem entra no loop debuscar_publicacoes. Campos cujo tipo é'image'e valor não-vazio recebem$_GESTOR['url-raiz']via*_widget_prefixar_url_raiz(), que preserva URLs já absolutas (http/https/protocol-relative/data:) e evita barra dupla (divergência defensável do req, que pedia prefixo incondicional).
Notas de execução (BATCH-043): (a) A reordenação manual usa um mapa page_id => item e itera selected_items, então IDs inexistentes, inativos ou sem correspondência em publisher_pages (INNER JOIN) são silenciosamente descartados — mas a contagem sem busca segue count(selected_items) literal por requisito (§1.2). (b) A inserção do campo "Variável do Widget" nos 24 HTMLs foi feita por script PowerShell (UTF-8 sem BOM, acentos via [char] para sobreviver ao PS 5.1) inserindo o bloco antes do <div style="border:1px solid #ccc;"> comum aos 24 arquivos. (c) Os 4 JS administrativos compartilham o mesmo handler de cópia (delegado, registrado uma vez), diferindo só no MODULO da assinatura. (d) A prefixagem de imagem (§6) lê o tipo do fields_schema da tabela publisher (não de publisher_pages); o teste integrado passou a criar/popular também a tabela publisher no seed (noticias com resumo=text, imagem=image) — necessário porque montar_itens agora a consulta em todos os testes integrados. (e) Validação: php -l/node --check/JSON OK; PHPUnit 40/40 (132 asserts) com MySQL conn2flow_test incluindo os testes da curadoria manual (ordem/slice/busca/contagem), item_casa_busca, prefixar_url_raiz (puro) e prefixagem de imagem (integrado, relativo→raiz / absoluto preservado / text intacto); Vitest 3/3 OK.
DEC-058 - 2026-06-15 - accepted
Prefixagem Dinâmica de URL Raiz para Campos de Imagem nos Widgets (req-043 / BATCH-043). Decisões de design:
-
Recuperação de Tipos de Campo via Schema: A tabela
publisherguarda o schema de campos no campofields_schema(JSON). Para identificar quais campos customizados de uma publicação correspondem a imagens, o sistema deve recuperar este schema usando opublisher_id(natural key) e a linguagem corrente ($_GESTOR['linguagem-codigo']), mapeando os IDs de campos que possuem o tipo"image". -
Otimização por Cache em Memória: Para evitar que o sistema execute consultas repetitivas ao banco de dados ao processar múltiplos itens do mesmo publicador (no mesmo request/render), os schemas decodificados devem ser armazenados em cache estático por chave composta (
publisher_id+language). -
Prefixagem Dinâmica nos Widgets:
-
No
publisher-index: A assinatura da função helper de montagem de itens (publisher_index_widget_montar_itens()) passa a aceitar$publisher_id. Seus chamadores (buscar_publicacoeselista_manual) fornecem o ID. Durante a conversão de campos customizados, caso o tipo do campo no schema do publicador seja'image'e ele contenha valor, a URL raiz ($_GESTOR['url-raiz']) é concatenada automaticamente. -
No
publisher-highlights: Na rotina de busca de publicações (publisher_highlights_widget_buscar_publicacoes()), após decodificar ofields_values, realiza-se a mesma verificação baseada no schema do publicador para prependir a URL raiz aos campos do tipo'image'.
-
Notas de execução (BATCH-043): A mudança visa garantir que os novos templates que utilizam @[[item#imagem]]@ (ou campos mapeados equivalentes de imagem) recebam o caminho absoluto ou a partir da raiz do site configurada na variável global $_GESTOR['url-raiz'] em tempo de execução.
DEC-059 - 2026-06-16 - accepted
Correção de Caracteres Especiais nos Widgets, Suporte AJAX no Preview e Refatoração de Módulos (req-044 / BATCH-044). Decisões de design:
-
Assinatura de widget fora do DOM (mapa em memória): A causa-raiz da corrupção (
menus->render/&gt;/") era persistir a assinatura crua (->, aspas, chaves) no atributodata-widget-signature, que o navegador re-escapa ao serializar/reparsear oinnerHTML. O atributo foi removido; a assinatura passa a viver apenas emthis.widgetsMap(objeto em memória doHtmlEditor), chaveado por umdata-widget-idlimpo (widget-N, contadorthis.widgetCounter). No DOM ficam só atributos alfanuméricos (data-widget-id/data-widget-type/data-widget-slug/data-widget-variable).getWidgetSignature(wrapper)resolve pelo mapa (fallback reconstruindo de type/slug). -
Diferenciação variável vs comentário na carga:
convertWidgetCommentsToWrappers()converte[[widgets#...]]/@[[widgets#...]]@em comentários temporários rotuladoswidgets-var#(distintos dos comentários reaiswidgets#), marcandoisVariableno mapa. No save (extractUserHtml),isVariable=truereconstrói a variável[[widgets#signature]]eisVariable=falsereconstrói o par de comentários com o mockup — corrigindo a perda do formato de variável reportada no req §1.1. -
Unescape de entidades (incl. duplo escape): helper
htmlUnescape(nohtml-editor.js) eunescapeEntities(interno aowidgetPreviewBootstrap, injetado via.toString()) usam<textarea>(RCDATA) em laço idempotente (até 3 passes) para decodificar>/"/&e o duplo escape&gt;, aplicado antes de processar/rotear a assinatura. -
Save de variável sem re-escape (token): como
container.innerHTMLre-escaparia>/&de um text node, o save substitui o wrapper-variável por um token alfanumérico (__C2F_WVAR_N__) e troca o token pela string[[widgets#signature]]na string final — preservando->/aspas intactos. -
Edição/clonagem com novo id:
editWidgetWrappergera um novodata-widget-ide copia os metadados anteriores ao atualizar o slug, evitando que clones na tela compartilhem a mesma entrada do mapa. -
*Inclusão automática e desduplicada de `.widget.js
no preview**:previewHtmlConteudoextrai as assinaturas presentes (extrairAssinaturasWidgets) e injeta no<head>do iframe os scripts controladores{raiz}{modulo}/widget.js(mapa fixogalleries/publisher-index/menus), uma única tag por módulo. URL no padrão usado pormenus.js/galleries.js/publisher-index.js(gestor.raiz + modulo + '/widget.js'`). -
widgetsToAjaxreplicado no iframe: como osrcdocé gerado estaticamente em JS (o PHP não cria a variável nesse contexto),montarWidgetAssetsHeadinjetawindow.gestor = Object.assign({}, window.parent.gestor); window.gestor.widgetsToAjax = "SIG1<#;>SIG2…"com as assinaturas únicas. Contrato confirmado emwidgets.php: cada item dewidgetsToAjax(divisor<#;>) é a assinatura completa repassada awidgets_getcomo$id. -
Refatoração para
html-editor-modules.js(divergência defensável do req §5.2): as 26 simulações demenus/galleries/publisher(+MENUS_SIM_FALLBACK/GALLERIES_SIM_FALLBACK+ a var de estadopublisher_table_tr_skeleton) foram extraídas para o novo arquivo, anexadas aowindow, carregado antes do interface (html-editor.php). Além das 3 variáveis pedidas pelo req (CodeMirrorHtml/CodeMirrorHtmlExtraHead/publisher_fields_schema), a análise de dependências mostrou que as funções movidas também usam as auxiliaresframeworkCSS/previewHtml/regexVariaveisGlobal/alvoUsaItemVars(que permanecem no interface) — todas igualmente expostas nowindowpara que as referências por nome nu resolvam em runtime. Os itens intercalados não-listados (getUpdatedHtmlWithValues,window.publisher_*_update_target_variables,addVariableSkeleton) permaneceram no interface.
Notas de execução (BATCH-044): (a) A extração do Slice 5 foi feita por script Node temporário (localização por nome no nível de indentação 4 + fim por linha }/];, mais comentários contíguos acima), preservando o código verbatim e removendo as exposições obsoletas (window.publisherValuesUpdate/window.publisherGetAllVariables, agora feitas pelo modules.js). (b) publisher_fields_schema é compartilhado por referência de objeto (mutação de .template_map no interface é visível no modules.js); nunca é reatribuído. (c) Validação: node --check em 5 JS, php -l no html-editor.php, Vitest 3/3 e PHPUnit 40/40 (112 asserts, 4 skip de DB gated). Deploy/runtime pendentes com o operador.
DEC-060 - 2026-06-16 - accepted
Correção de Temporal Dead Zone (TDZ) na inicialização do Editor HTML Visual (req-045 / BATCH-045). Slice corretivo do BATCH-044. Decisão:
-
Causa-raiz:
const/letno meio do arquivo lidas por init síncrona:html-editor-interface.jsroda inteiro dentro de$(document).ready(...). Aconst WIDGET_SCRIPT_MODULES(introduzida no BATCH-044) e alet total_sessoesficam no meio do arquivo (~L1544/~L2056). Comoconst/letnão são hoisted (ao contrário devar/function), qualquer código de inicialização que rode ANTES dessas linhas e cascateie empreviewHtml()→previewHtmlConteudo()→montarWidgetAssetsHead()(lêWIDGET_SCRIPT_MODULES) ou empageModificationContainerMove()(lêtotal_sessoesnumsetTimeout) bate na Temporal Dead Zone →ReferenceError, que aborta o resto doready. -
Dois gatilhos síncronos, não um: o req-045 atribuía o erro apenas à chamada síncrona
contentPageTabHandler(). O runtime provou que mover só esse não bastou: o gatilho principal é oonLoadque o Fomantic dispara SÍNCRONAMENTE ao inicializar$('.menuContainerPagina .item').tab({...})(a init do tab executachangeTab→onLoadpara a aba ativa imediatamente). EsseonLoadchamapreviewHtml()epageModificationContainerMove(). Logo, ambos (ocontentPageTabHandler()e a init do.tab()) precisam rodar após as declarações. -
Solução: init do tab + kickoff no fim do
ready: remover do meio do arquivo tanto a invocaçãocontentPageTabHandler()quanto o bloco$('.menuContainerPagina .item').tab({onLoad...}), e colocá-los no fim doready, após todas as declarações locais — preservando a ordem original (handler antes da init do tab; a chamada.tab('change tab')dentro do handler permanece no-op enquanto o tab não está inicializado, idêntico ao comportamento anterior). O outro tab (.menuPaginas, ~L688) não foi movido: seuonLoadsó fazCodeMirror*.refresh()(não toca as consts tardias). Inits intermediárias (dropdowns.frameworkCSS/.publisher-design-mode-*, ~L830-849) apenas registram callbacksonChange(não disparam na carga). Alternativa rejeitada (mover só as 2 declarações para o topo / inline): mais frágil e pontual; mover os gatilhos para o fim é a invariante correta e cobre a classe inteira.versaoda bibliotecahtml-editorbumpada pelo operador para cache-bust (1.3.x). Validação:node --checkOK + verificação de quecontentPageTabHandler()e a init do.tab()são as únicas execuções síncronas que cascateiam empreviewHtmle estão ambas no fim; runtime (console semReferenceError, troca de abas e preview de widgets) pendente com o operador. -
Overlays do editor destruídos por
body.innerHTMLno caso de widget-variável (html-editor.js): bug colateral correlato, encontrado em runtime. Na init doHtmlEditor(dentro do iframe),createOverlays/createToolbar/createPlaceholderanexam os overlays/toolbar/breadcrumb/styler aodocument.bodyANTES deconvertWidgetCommentsToWrappers(). Esse método convertia variáveis inline[[widgets#...]]em comentários viadocument.body.innerHTML = bodyHtml.replace(...), o que destrói todos os filhos do body — inclusive a UI do editor — quebrando a seleção de elementos. Só ocorria com widget em formato de variável (no formato de comentário oinnerHTMLnão era reescrito). Solução: novoconvertWidgetVariablesToComments()que faz a conversão de forma CIRÚRGICA, percorrendo só os text nodes do conteúdo (TreeWalkerSHOW_TEXT, pulando a UIhtml-editor-*) e substituindo cada[[widgets#...]]por um par de comentárioswidgets-var#viareplaceChildde um fragmento — sem nunca reescreverbody.innerHTML. Preserva a correção de caracteres especiais do req-044 (a assinatura vem limpa donodeValue). OwidgetPreviewBootstrap(iframe de preview) mantém o reset de innerHTML pois lá não há UI persistente a preservar.